aboutsummaryrefslogtreecommitdiff
path: root/pkg/spec/namespaces.go
diff options
context:
space:
mode:
Diffstat (limited to 'pkg/spec/namespaces.go')
-rw-r--r--pkg/spec/namespaces.go433
1 files changed, 433 insertions, 0 deletions
diff --git a/pkg/spec/namespaces.go b/pkg/spec/namespaces.go
new file mode 100644
index 000000000..a45137416
--- /dev/null
+++ b/pkg/spec/namespaces.go
@@ -0,0 +1,433 @@
+package createconfig
+
+import (
+ "net"
+ "os"
+ "strconv"
+ "strings"
+
+ "github.com/containers/libpod/libpod"
+ "github.com/containers/libpod/libpod/define"
+ "github.com/containers/libpod/pkg/cgroups"
+ "github.com/cri-o/ocicni/pkg/ocicni"
+ "github.com/docker/go-connections/nat"
+ spec "github.com/opencontainers/runtime-spec/specs-go"
+ "github.com/opencontainers/runtime-tools/generate"
+ "github.com/pkg/errors"
+ "github.com/sirupsen/logrus"
+)
+
+func (c *NetworkConfig) ToCreateOptions(runtime *libpod.Runtime, userns *UserConfig) ([]libpod.CtrCreateOption, error) {
+ var portBindings []ocicni.PortMapping
+ var err error
+ if len(c.PortBindings) > 0 {
+ portBindings, err = NatToOCIPortBindings(c.PortBindings)
+ if err != nil {
+ return nil, errors.Wrapf(err, "unable to create port bindings")
+ }
+ }
+
+ options := make([]libpod.CtrCreateOption, 0)
+ userNetworks := c.NetMode.UserDefined()
+ networks := make([]string, 0)
+
+ if IsPod(userNetworks) {
+ userNetworks = ""
+ }
+ if userNetworks != "" {
+ for _, netName := range strings.Split(userNetworks, ",") {
+ if netName == "" {
+ return nil, errors.Errorf("container networks %q invalid", userNetworks)
+ }
+ networks = append(networks, netName)
+ }
+ }
+
+ if c.NetMode.IsNS() {
+ ns := c.NetMode.NS()
+ if ns == "" {
+ return nil, errors.Errorf("invalid empty user-defined network namespace")
+ }
+ _, err := os.Stat(ns)
+ if err != nil {
+ return nil, err
+ }
+ } else if c.NetMode.IsContainer() {
+ connectedCtr, err := runtime.LookupContainer(c.NetMode.Container())
+ if err != nil {
+ return nil, errors.Wrapf(err, "container %q not found", c.NetMode.Container())
+ }
+ options = append(options, libpod.WithNetNSFrom(connectedCtr))
+ } else if !c.NetMode.IsHost() && !c.NetMode.IsNone() {
+ postConfigureNetNS := userns.getPostConfigureNetNS()
+ options = append(options, libpod.WithNetNS(portBindings, postConfigureNetNS, string(c.NetMode), networks))
+ }
+
+ if len(c.DNSSearch) > 0 {
+ options = append(options, libpod.WithDNSSearch(c.DNSSearch))
+ }
+ if len(c.DNSServers) > 0 {
+ if len(c.DNSServers) == 1 && strings.ToLower(c.DNSServers[0]) == "none" {
+ options = append(options, libpod.WithUseImageResolvConf())
+ } else {
+ options = append(options, libpod.WithDNS(c.DNSServers))
+ }
+ }
+ if len(c.DNSOpt) > 0 {
+ options = append(options, libpod.WithDNSOption(c.DNSOpt))
+ }
+ if c.IPAddress != "" {
+ ip := net.ParseIP(c.IPAddress)
+ if ip == nil {
+ return nil, errors.Wrapf(define.ErrInvalidArg, "cannot parse %s as IP address", c.IPAddress)
+ } else if ip.To4() == nil {
+ return nil, errors.Wrapf(define.ErrInvalidArg, "%s is not an IPv4 address", c.IPAddress)
+ }
+ options = append(options, libpod.WithStaticIP(ip))
+ }
+
+ if c.MacAddress != "" {
+ mac, err := net.ParseMAC(c.MacAddress)
+ if err != nil {
+ return nil, errors.Wrapf(define.ErrInvalidArg, "cannot parse %s as MAC address: %v", c.MacAddress, err)
+ }
+ options = append(options, libpod.WithStaticMAC(mac))
+ }
+
+ return options, nil
+}
+
+func (c *NetworkConfig) ConfigureGenerator(g *generate.Generator) error {
+ netMode := c.NetMode
+ if netMode.IsHost() {
+ logrus.Debug("Using host netmode")
+ if err := g.RemoveLinuxNamespace(string(spec.NetworkNamespace)); err != nil {
+ return err
+ }
+ } else if netMode.IsNone() {
+ logrus.Debug("Using none netmode")
+ } else if netMode.IsBridge() {
+ logrus.Debug("Using bridge netmode")
+ } else if netCtr := netMode.Container(); netCtr != "" {
+ logrus.Debugf("using container %s netmode", netCtr)
+ } else if IsNS(string(netMode)) {
+ logrus.Debug("Using ns netmode")
+ if err := g.AddOrReplaceLinuxNamespace(string(spec.NetworkNamespace), NS(string(netMode))); err != nil {
+ return err
+ }
+ } else if IsPod(string(netMode)) {
+ logrus.Debug("Using pod netmode, unless pod is not sharing")
+ } else if netMode.IsSlirp4netns() {
+ logrus.Debug("Using slirp4netns netmode")
+ } else if netMode.IsUserDefined() {
+ logrus.Debug("Using user defined netmode")
+ } else {
+ return errors.Errorf("unknown network mode")
+ }
+
+ if c.HTTPProxy {
+ for _, envSpec := range []string{
+ "http_proxy",
+ "HTTP_PROXY",
+ "https_proxy",
+ "HTTPS_PROXY",
+ "ftp_proxy",
+ "FTP_PROXY",
+ "no_proxy",
+ "NO_PROXY",
+ } {
+ envVal := os.Getenv(envSpec)
+ if envVal != "" {
+ g.AddProcessEnv(envSpec, envVal)
+ }
+ }
+ }
+
+ if g.Config.Annotations == nil {
+ g.Config.Annotations = make(map[string]string)
+ }
+
+ if c.PublishAll {
+ g.Config.Annotations[libpod.InspectAnnotationPublishAll] = libpod.InspectResponseTrue
+ } else {
+ g.Config.Annotations[libpod.InspectAnnotationPublishAll] = libpod.InspectResponseFalse
+ }
+
+ return nil
+}
+
+// NatToOCIPortBindings iterates a nat.portmap slice and creates []ocicni portmapping slice
+func NatToOCIPortBindings(ports nat.PortMap) ([]ocicni.PortMapping, error) {
+ var portBindings []ocicni.PortMapping
+ for containerPb, hostPb := range ports {
+ var pm ocicni.PortMapping
+ pm.ContainerPort = int32(containerPb.Int())
+ for _, i := range hostPb {
+ var hostPort int
+ var err error
+ pm.HostIP = i.HostIP
+ if i.HostPort == "" {
+ hostPort = containerPb.Int()
+ } else {
+ hostPort, err = strconv.Atoi(i.HostPort)
+ if err != nil {
+ return nil, errors.Wrapf(err, "unable to convert host port to integer")
+ }
+ }
+
+ pm.HostPort = int32(hostPort)
+ pm.Protocol = containerPb.Proto()
+ portBindings = append(portBindings, pm)
+ }
+ }
+ return portBindings, nil
+}
+
+func (c *CgroupConfig) ToCreateOptions(runtime *libpod.Runtime) ([]libpod.CtrCreateOption, error) {
+ options := make([]libpod.CtrCreateOption, 0)
+ if c.CgroupMode.IsNS() {
+ ns := c.CgroupMode.NS()
+ if ns == "" {
+ return nil, errors.Errorf("invalid empty user-defined network namespace")
+ }
+ _, err := os.Stat(ns)
+ if err != nil {
+ return nil, err
+ }
+ } else if c.CgroupMode.IsContainer() {
+ connectedCtr, err := runtime.LookupContainer(c.CgroupMode.Container())
+ if err != nil {
+ return nil, errors.Wrapf(err, "container %q not found", c.CgroupMode.Container())
+ }
+ options = append(options, libpod.WithCgroupNSFrom(connectedCtr))
+ }
+
+ if c.CgroupParent != "" {
+ options = append(options, libpod.WithCgroupParent(c.CgroupParent))
+ }
+
+ if c.Cgroups == "disabled" {
+ options = append(options, libpod.WithNoCgroups())
+ }
+
+ return options, nil
+}
+
+func (c *UserConfig) ToCreateOptions(runtime *libpod.Runtime) ([]libpod.CtrCreateOption, error) {
+ options := make([]libpod.CtrCreateOption, 0)
+ if c.UsernsMode.IsNS() {
+ ns := c.UsernsMode.NS()
+ if ns == "" {
+ return nil, errors.Errorf("invalid empty user-defined user namespace")
+ }
+ _, err := os.Stat(ns)
+ if err != nil {
+ return nil, err
+ }
+ options = append(options, libpod.WithIDMappings(*c.IDMappings))
+ } else if c.UsernsMode.IsContainer() {
+ connectedCtr, err := runtime.LookupContainer(c.UsernsMode.Container())
+ if err != nil {
+ return nil, errors.Wrapf(err, "container %q not found", c.UsernsMode.Container())
+ }
+ options = append(options, libpod.WithUserNSFrom(connectedCtr))
+ } else {
+ options = append(options, libpod.WithIDMappings(*c.IDMappings))
+ }
+
+ options = append(options, libpod.WithUser(c.User))
+ options = append(options, libpod.WithGroups(c.GroupAdd))
+
+ return options, nil
+}
+
+func (c *UserConfig) ConfigureGenerator(g *generate.Generator) error {
+ if IsNS(string(c.UsernsMode)) {
+ if err := g.AddOrReplaceLinuxNamespace(string(spec.UserNamespace), NS(string(c.UsernsMode))); err != nil {
+ return err
+ }
+ // runc complains if no mapping is specified, even if we join another ns. So provide a dummy mapping
+ g.AddLinuxUIDMapping(uint32(0), uint32(0), uint32(1))
+ g.AddLinuxGIDMapping(uint32(0), uint32(0), uint32(1))
+ }
+
+ if (len(c.IDMappings.UIDMap) > 0 || len(c.IDMappings.GIDMap) > 0) && !c.UsernsMode.IsHost() {
+ if err := g.AddOrReplaceLinuxNamespace(string(spec.UserNamespace), ""); err != nil {
+ return err
+ }
+ }
+ for _, uidmap := range c.IDMappings.UIDMap {
+ g.AddLinuxUIDMapping(uint32(uidmap.HostID), uint32(uidmap.ContainerID), uint32(uidmap.Size))
+ }
+ for _, gidmap := range c.IDMappings.GIDMap {
+ g.AddLinuxGIDMapping(uint32(gidmap.HostID), uint32(gidmap.ContainerID), uint32(gidmap.Size))
+ }
+ return nil
+}
+
+func (c *UserConfig) getPostConfigureNetNS() bool {
+ hasUserns := c.UsernsMode.IsContainer() || c.UsernsMode.IsNS() || len(c.IDMappings.UIDMap) > 0 || len(c.IDMappings.GIDMap) > 0
+ postConfigureNetNS := hasUserns && !c.UsernsMode.IsHost()
+ return postConfigureNetNS
+}
+
+func (c *UserConfig) InNS(isRootless bool) bool {
+ hasUserns := c.UsernsMode.IsContainer() || c.UsernsMode.IsNS() || len(c.IDMappings.UIDMap) > 0 || len(c.IDMappings.GIDMap) > 0
+ return isRootless || (hasUserns && !c.UsernsMode.IsHost())
+}
+
+func (c *IpcConfig) ToCreateOptions(runtime *libpod.Runtime) ([]libpod.CtrCreateOption, error) {
+ options := make([]libpod.CtrCreateOption, 0)
+ if c.IpcMode.IsHost() {
+ options = append(options, libpod.WithShmDir("/dev/shm"))
+ } else if c.IpcMode.IsContainer() {
+ connectedCtr, err := runtime.LookupContainer(c.IpcMode.Container())
+ if err != nil {
+ return nil, errors.Wrapf(err, "container %q not found", c.IpcMode.Container())
+ }
+
+ options = append(options, libpod.WithIPCNSFrom(connectedCtr))
+ options = append(options, libpod.WithShmDir(connectedCtr.ShmDir()))
+ }
+
+ return options, nil
+}
+
+func (c *IpcConfig) ConfigureGenerator(g *generate.Generator) error {
+ ipcMode := c.IpcMode
+ if IsNS(string(ipcMode)) {
+ return g.AddOrReplaceLinuxNamespace(string(spec.IPCNamespace), NS(string(ipcMode)))
+ }
+ if ipcMode.IsHost() {
+ return g.RemoveLinuxNamespace(string(spec.IPCNamespace))
+ }
+ if ipcCtr := ipcMode.Container(); ipcCtr != "" {
+ logrus.Debugf("Using container %s ipcmode", ipcCtr)
+ }
+
+ return nil
+}
+
+func (c *CgroupConfig) ConfigureGenerator(g *generate.Generator) error {
+ cgroupMode := c.CgroupMode
+ if cgroupMode.IsDefaultValue() {
+ // If the value is not specified, default to "private" on cgroups v2 and "host" on cgroups v1.
+ unified, err := cgroups.IsCgroup2UnifiedMode()
+ if err != nil {
+ return err
+ }
+ if unified {
+ cgroupMode = "private"
+ } else {
+ cgroupMode = "host"
+ }
+ }
+ if cgroupMode.IsNS() {
+ return g.AddOrReplaceLinuxNamespace(string(spec.CgroupNamespace), NS(string(cgroupMode)))
+ }
+ if cgroupMode.IsHost() {
+ return g.RemoveLinuxNamespace(string(spec.CgroupNamespace))
+ }
+ if cgroupMode.IsPrivate() {
+ return g.AddOrReplaceLinuxNamespace(string(spec.CgroupNamespace), "")
+ }
+ if cgCtr := cgroupMode.Container(); cgCtr != "" {
+ logrus.Debugf("Using container %s cgroup mode", cgCtr)
+ }
+ return nil
+}
+
+func (c *PidConfig) ToCreateOptions(runtime *libpod.Runtime) ([]libpod.CtrCreateOption, error) {
+ options := make([]libpod.CtrCreateOption, 0)
+ if c.PidMode.IsContainer() {
+ connectedCtr, err := runtime.LookupContainer(c.PidMode.Container())
+ if err != nil {
+ return nil, errors.Wrapf(err, "container %q not found", c.PidMode.Container())
+ }
+
+ options = append(options, libpod.WithPIDNSFrom(connectedCtr))
+ }
+
+ return options, nil
+}
+
+func (c *PidConfig) ConfigureGenerator(g *generate.Generator) error {
+ pidMode := c.PidMode
+ if IsNS(string(pidMode)) {
+ return g.AddOrReplaceLinuxNamespace(string(spec.PIDNamespace), NS(string(pidMode)))
+ }
+ if pidMode.IsHost() {
+ return g.RemoveLinuxNamespace(string(spec.PIDNamespace))
+ }
+ if pidCtr := pidMode.Container(); pidCtr != "" {
+ logrus.Debugf("using container %s pidmode", pidCtr)
+ }
+ if IsPod(string(pidMode)) {
+ logrus.Debug("using pod pidmode")
+ }
+ return nil
+}
+
+func (c *UtsConfig) ToCreateOptions(runtime *libpod.Runtime, pod *libpod.Pod) ([]libpod.CtrCreateOption, error) {
+ options := make([]libpod.CtrCreateOption, 0)
+ if IsPod(string(c.UtsMode)) {
+ options = append(options, libpod.WithUTSNSFromPod(pod))
+ }
+ if c.UtsMode.IsContainer() {
+ connectedCtr, err := runtime.LookupContainer(c.UtsMode.Container())
+ if err != nil {
+ return nil, errors.Wrapf(err, "container %q not found", c.UtsMode.Container())
+ }
+
+ options = append(options, libpod.WithUTSNSFrom(connectedCtr))
+ }
+ if c.NoHosts {
+ options = append(options, libpod.WithUseImageHosts())
+ }
+ if len(c.HostAdd) > 0 && !c.NoHosts {
+ options = append(options, libpod.WithHosts(c.HostAdd))
+ }
+
+ return options, nil
+}
+
+func (c *UtsConfig) ConfigureGenerator(g *generate.Generator, net *NetworkConfig, runtime *libpod.Runtime) error {
+ hostname := c.Hostname
+ var err error
+ if hostname == "" {
+ if utsCtrID := c.UtsMode.Container(); utsCtrID != "" {
+ utsCtr, err := runtime.GetContainer(utsCtrID)
+ if err != nil {
+ return errors.Wrapf(err, "unable to retrieve hostname from dependency container %s", utsCtrID)
+ }
+ hostname = utsCtr.Hostname()
+ } else if net.NetMode.IsHost() || c.UtsMode.IsHost() {
+ hostname, err = os.Hostname()
+ if err != nil {
+ return errors.Wrap(err, "unable to retrieve hostname of the host")
+ }
+ } else {
+ logrus.Debug("No hostname set; container's hostname will default to runtime default")
+ }
+ }
+ g.RemoveHostname()
+ if c.Hostname != "" || !c.UtsMode.IsHost() {
+ // Set the hostname in the OCI configuration only
+ // if specified by the user or if we are creating
+ // a new UTS namespace.
+ g.SetHostname(hostname)
+ }
+ g.AddProcessEnv("HOSTNAME", hostname)
+
+ utsMode := c.UtsMode
+ if IsNS(string(utsMode)) {
+ return g.AddOrReplaceLinuxNamespace(string(spec.UTSNamespace), NS(string(utsMode)))
+ }
+ if utsMode.IsHost() {
+ return g.RemoveLinuxNamespace(string(spec.UTSNamespace))
+ }
+ if utsCtr := utsMode.Container(); utsCtr != "" {
+ logrus.Debugf("using container %s utsmode", utsCtr)
+ }
+ return nil
+}