1
0
mirror of https://github.com/lxc/crio-lxc.git synced 2026-02-05 09:45:04 +01:00
Files
crio-lxc/namespaces.go
Ruben Jenster c6a3f47316 exec: Add namespace flags. Implements #50
Signed-off-by: Ruben Jenster <r.jenster@drachenfels.de>
2021-04-30 18:00:16 +02:00

162 lines
4.4 KiB
Go

package lxcri
import (
"fmt"
"os"
"runtime"
"strings"
"github.com/opencontainers/runtime-spec/specs-go"
"golang.org/x/sys/unix"
)
// namespace is a mapping from the namespace name
// as used in /proc/{pid}/ns and the namespace clone flag,
// as defined in `man 2 clone`.
type namespace struct {
Name string
CloneFlag int
}
var (
cgroupNamespace = namespace{"cgroup", unix.CLONE_NEWCGROUP}
ipcNamespace = namespace{"ipc", unix.CLONE_NEWIPC}
mountNamespace = namespace{"mnt", unix.CLONE_NEWNS}
networkNamespace = namespace{"net", unix.CLONE_NEWNET}
pidNamespace = namespace{"pid", unix.CLONE_NEWPID}
timeNamespace = namespace{"time", unix.CLONE_NEWTIME}
userNamespace = namespace{"user", unix.CLONE_NEWUSER}
utsNamespace = namespace{"uts", unix.CLONE_NEWUTS}
namespaceMap = map[specs.LinuxNamespaceType]namespace{
specs.CgroupNamespace: cgroupNamespace,
specs.IPCNamespace: ipcNamespace,
specs.MountNamespace: mountNamespace,
specs.NetworkNamespace: networkNamespace,
specs.PIDNamespace: pidNamespace,
// specs.TimeNamespace: timeNamespace,
specs.UserNamespace: userNamespace,
specs.UTSNamespace: utsNamespace,
}
)
func configureNamespaces(c *Container) error {
seenNamespaceTypes := map[specs.LinuxNamespaceType]bool{}
cloneNamespaces := make([]string, 0, len(c.Spec.Linux.Namespaces))
for _, ns := range c.Spec.Linux.Namespaces {
if _, seen := seenNamespaceTypes[ns.Type]; seen {
return fmt.Errorf("duplicate namespace %s", ns.Type)
}
seenNamespaceTypes[ns.Type] = true
n, supported := namespaceMap[ns.Type]
if !supported {
return fmt.Errorf("unsupported namespace %s", ns.Type)
}
if ns.Path == "" {
cloneNamespaces = append(cloneNamespaces, n.Name)
continue
}
configKey := fmt.Sprintf("lxc.namespace.share.%s", n.Name)
if err := c.setConfigItem(configKey, ns.Path); err != nil {
return err
}
}
return c.setConfigItem("lxc.namespace.clone", strings.Join(cloneNamespaces, " "))
}
func isNamespaceEnabled(spec *specs.Spec, nsType specs.LinuxNamespaceType) bool {
for _, ns := range spec.Linux.Namespaces {
if ns.Type == nsType {
return true
}
}
return false
}
func getNamespace(spec *specs.Spec, nsType specs.LinuxNamespaceType) *specs.LinuxNamespace {
for _, n := range spec.Linux.Namespaces {
if n.Type == nsType {
return &n
}
}
return nil
}
// isNamespaceSharedWithHost returns true if the given namespace is nil.
// If the given namespace is not nil then the namespace then true is
// returned if the namespace path refers to the host namespace and
// false otherwise.
// Should be used with isNamespaceSharedWithHost(getNamespace(...))
func isNamespaceSharedWithRuntime(ns *specs.LinuxNamespace) (bool, error) {
// no namespace with this name defined
if ns == nil {
return true, nil
}
// namespaces without a target path are cloned
if ns.Path == "" {
return false, nil
}
// from `man namespaces` The /proc/[pid]/ns/ directory [...]
// In Linux 3.7 and earlier, these files were visible as hard links.
// If two processes are in the same namespace, then the device IDs and inode numbers
// of their /proc/[pid]/ns/xxx symbolic links will be the same;
// anapplication can check this using the stat.st_dev and stat.st_ino
// fields returned by stat(2).
// e.g `strace /usr/bin/stat -L /proc/1/ns/pid`
var stat unix.Stat_t
err := unix.Stat(ns.Path, &stat)
if err != nil {
return false, err
}
var stat1 unix.Stat_t
err = unix.Stat("/proc/self/ns/pid", &stat1)
if err != nil {
return false, err
}
sameNS := (stat.Dev == stat1.Dev) && (stat.Ino == stat1.Ino)
return sameNS, nil
}
// lxc does not set the hostname on shared namespaces
func setHostname(nsPath string, hostname string) error {
// setns only affects the current thread
runtime.LockOSThread()
defer runtime.UnlockOSThread()
f, err := os.Open(nsPath)
if err != nil {
return fmt.Errorf("failed to open container uts namespace %q: %w", nsPath, err)
}
// #nosec
defer f.Close()
self, err := os.Open("/proc/self/ns/uts")
if err != nil {
return fmt.Errorf("failed to open uts namespace : %w", err)
}
// #nosec
defer func() {
unix.Setns(int(self.Fd()), unix.CLONE_NEWUTS)
self.Close()
}()
err = unix.Setns(int(f.Fd()), unix.CLONE_NEWUTS)
if err != nil {
return fmt.Errorf("failed to switch to UTS namespace %s: %w", nsPath, err)
}
err = unix.Sethostname([]byte(hostname))
if err != nil {
return fmt.Errorf("unix.Sethostname failed: %w", err)
}
return nil
}