mirror of
https://github.com/opencontainers/runc.git
synced 2025-09-27 03:46:19 +08:00

This updates the current list to what we have now in docker and also makes these always added so that these are masked out. Privileged containers can always unmount these if they want to read from kcore or something like that. Signed-off-by: Michael Crosby <crosbymichael@gmail.com>
337 lines
7.3 KiB
Go
337 lines
7.3 KiB
Go
// +build linux
|
|
|
|
package main
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"syscall"
|
|
|
|
"github.com/Sirupsen/logrus"
|
|
"github.com/codegangsta/cli"
|
|
"github.com/opencontainers/runc/libcontainer"
|
|
"github.com/opencontainers/runc/libcontainer/configs"
|
|
"github.com/opencontainers/specs"
|
|
)
|
|
|
|
const wildcard = -1
|
|
|
|
var errEmptyID = errors.New("container id cannot be empty")
|
|
|
|
var allowedDevices = []*configs.Device{
|
|
// allow mknod for any device
|
|
{
|
|
Type: 'c',
|
|
Major: wildcard,
|
|
Minor: wildcard,
|
|
Permissions: "m",
|
|
Allow: true,
|
|
},
|
|
{
|
|
Type: 'b',
|
|
Major: wildcard,
|
|
Minor: wildcard,
|
|
Permissions: "m",
|
|
Allow: true,
|
|
},
|
|
{
|
|
Type: 'c',
|
|
Path: "/dev/null",
|
|
Major: 1,
|
|
Minor: 3,
|
|
Permissions: "rwm",
|
|
Allow: true,
|
|
},
|
|
{
|
|
Type: 'c',
|
|
Path: "/dev/random",
|
|
Major: 1,
|
|
Minor: 8,
|
|
Permissions: "rwm",
|
|
Allow: true,
|
|
},
|
|
{
|
|
Type: 'c',
|
|
Path: "/dev/full",
|
|
Major: 1,
|
|
Minor: 7,
|
|
Permissions: "rwm",
|
|
Allow: true,
|
|
},
|
|
{
|
|
Type: 'c',
|
|
Path: "/dev/tty",
|
|
Major: 5,
|
|
Minor: 0,
|
|
Permissions: "rwm",
|
|
Allow: true,
|
|
},
|
|
{
|
|
Type: 'c',
|
|
Path: "/dev/zero",
|
|
Major: 1,
|
|
Minor: 5,
|
|
Permissions: "rwm",
|
|
Allow: true,
|
|
},
|
|
{
|
|
Type: 'c',
|
|
Path: "/dev/urandom",
|
|
Major: 1,
|
|
Minor: 9,
|
|
Permissions: "rwm",
|
|
Allow: true,
|
|
},
|
|
{
|
|
Path: "/dev/console",
|
|
Type: 'c',
|
|
Major: 5,
|
|
Minor: 1,
|
|
Permissions: "rwm",
|
|
Allow: true,
|
|
},
|
|
// /dev/pts/ - pts namespaces are "coming soon"
|
|
{
|
|
Path: "",
|
|
Type: 'c',
|
|
Major: 136,
|
|
Minor: wildcard,
|
|
Permissions: "rwm",
|
|
Allow: true,
|
|
},
|
|
{
|
|
Path: "",
|
|
Type: 'c',
|
|
Major: 5,
|
|
Minor: 2,
|
|
Permissions: "rwm",
|
|
Allow: true,
|
|
},
|
|
// tuntap
|
|
{
|
|
Path: "",
|
|
Type: 'c',
|
|
Major: 10,
|
|
Minor: 200,
|
|
Permissions: "rwm",
|
|
Allow: true,
|
|
},
|
|
}
|
|
|
|
var (
|
|
maskedPaths = []string{
|
|
"/proc/kcore",
|
|
"/proc/latency_stats",
|
|
"/proc/timer_stats",
|
|
"/proc/sched_debug",
|
|
}
|
|
readonlyPaths = []string{
|
|
"/proc/asound",
|
|
"/proc/bus",
|
|
"/proc/fs",
|
|
"/proc/irq",
|
|
"/proc/sys",
|
|
"/proc/sysrq-trigger",
|
|
}
|
|
)
|
|
|
|
var container libcontainer.Container
|
|
|
|
func containerPreload(context *cli.Context) error {
|
|
c, err := getContainer(context)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
container = c
|
|
return nil
|
|
}
|
|
|
|
// loadFactory returns the configured factory instance for execing containers.
|
|
func loadFactory(context *cli.Context) (libcontainer.Factory, error) {
|
|
root := context.GlobalString("root")
|
|
abs, err := filepath.Abs(root)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return libcontainer.New(abs, libcontainer.Cgroupfs, func(l *libcontainer.LinuxFactory) error {
|
|
l.CriuPath = context.GlobalString("criu")
|
|
return nil
|
|
})
|
|
}
|
|
|
|
// getContainer returns the specified container instance by loading it from state
|
|
// with the default factory.
|
|
func getContainer(context *cli.Context) (libcontainer.Container, error) {
|
|
id := context.Args().First()
|
|
if id == "" {
|
|
return nil, errEmptyID
|
|
}
|
|
factory, err := loadFactory(context)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return factory.Load(id)
|
|
}
|
|
|
|
// fatal prints the error's details if it is a libcontainer specific error type
|
|
// then exits the program with an exit status of 1.
|
|
func fatal(err error) {
|
|
if lerr, ok := err.(libcontainer.Error); ok {
|
|
lerr.Detail(os.Stderr)
|
|
os.Exit(1)
|
|
}
|
|
fmt.Fprintln(os.Stderr, err)
|
|
os.Exit(1)
|
|
}
|
|
|
|
func getDefaultImagePath(context *cli.Context) string {
|
|
cwd, err := os.Getwd()
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
return filepath.Join(cwd, "checkpoint")
|
|
}
|
|
|
|
// newProcess returns a new libcontainer Process with the arguments from the
|
|
// spec and stdio from the current process.
|
|
func newProcess(p specs.Process) *libcontainer.Process {
|
|
return &libcontainer.Process{
|
|
Args: p.Args,
|
|
Env: p.Env,
|
|
// TODO: fix libcontainer's API to better support uid/gid in a typesafe way.
|
|
User: fmt.Sprintf("%d:%d", p.User.UID, p.User.GID),
|
|
Cwd: p.Cwd,
|
|
}
|
|
}
|
|
|
|
func dupStdio(process *libcontainer.Process, rootuid int) error {
|
|
process.Stdin = os.Stdin
|
|
process.Stdout = os.Stdout
|
|
process.Stderr = os.Stderr
|
|
for _, fd := range []uintptr{
|
|
os.Stdin.Fd(),
|
|
os.Stdout.Fd(),
|
|
os.Stderr.Fd(),
|
|
} {
|
|
if err := syscall.Fchown(int(fd), rootuid, rootuid); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// If systemd is supporting sd_notify protocol, this function will add support
|
|
// for sd_notify protocol from within the container.
|
|
func setupSdNotify(spec *specs.LinuxSpec, notifySocket string) {
|
|
spec.Mounts = append(spec.Mounts, specs.Mount{Destination: notifySocket, Type: "bind", Source: notifySocket, Options: []string{"bind"}})
|
|
spec.Process.Env = append(spec.Process.Env, fmt.Sprintf("NOTIFY_SOCKET=%s", notifySocket))
|
|
}
|
|
|
|
func destroy(container libcontainer.Container) {
|
|
if err := container.Destroy(); err != nil {
|
|
logrus.Error(err)
|
|
}
|
|
}
|
|
|
|
func setupIO(process *libcontainer.Process, rootuid int, console string, createTTY, detach bool) (*tty, error) {
|
|
// detach and createTty will not work unless a console path is passed
|
|
// so error out here before changing any terminal settings
|
|
if createTTY && detach && console == "" {
|
|
return nil, fmt.Errorf("cannot allocate tty if runc will detach")
|
|
}
|
|
if createTTY {
|
|
return createTty(process, rootuid, console)
|
|
}
|
|
if detach {
|
|
if err := dupStdio(process, rootuid); err != nil {
|
|
return nil, err
|
|
}
|
|
return nil, nil
|
|
}
|
|
return createStdioPipes(process, rootuid)
|
|
}
|
|
|
|
func createPidFile(path string, process *libcontainer.Process) error {
|
|
pid, err := process.Pid()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
f, err := os.Create(path)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer f.Close()
|
|
_, err = fmt.Fprintf(f, "%d", pid)
|
|
return err
|
|
}
|
|
|
|
func createContainer(context *cli.Context, id string, spec *specs.LinuxSpec) (libcontainer.Container, error) {
|
|
config, err := createLibcontainerConfig(id, spec)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if _, err := os.Stat(config.Rootfs); err != nil {
|
|
if os.IsNotExist(err) {
|
|
return nil, fmt.Errorf("rootfs (%q) does not exist", config.Rootfs)
|
|
}
|
|
return nil, err
|
|
}
|
|
|
|
factory, err := loadFactory(context)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return factory.Create(id, config)
|
|
}
|
|
|
|
// runProcess will create a new process in the specified container
|
|
// by executing the process specified in the 'config'.
|
|
func runProcess(container libcontainer.Container, config *specs.Process, listenFDs []*os.File, console string, pidFile string, detach bool) (int, error) {
|
|
process := newProcess(*config)
|
|
|
|
// Add extra file descriptors if needed
|
|
if len(listenFDs) > 0 {
|
|
process.Env = append(process.Env, fmt.Sprintf("LISTEN_FDS=%d", len(listenFDs)), "LISTEN_PID=1")
|
|
process.ExtraFiles = append(process.ExtraFiles, listenFDs...)
|
|
}
|
|
|
|
rootuid, err := container.Config().HostUID()
|
|
if err != nil {
|
|
return -1, err
|
|
}
|
|
|
|
tty, err := setupIO(process, rootuid, console, config.Terminal, detach)
|
|
if err != nil {
|
|
return -1, err
|
|
}
|
|
|
|
handler := newSignalHandler(tty)
|
|
defer handler.Close()
|
|
|
|
if err := container.Start(process); err != nil {
|
|
if tty != nil {
|
|
tty.Close()
|
|
}
|
|
return -1, err
|
|
}
|
|
|
|
if pidFile != "" {
|
|
if err := createPidFile(pidFile, process); err != nil {
|
|
process.Signal(syscall.SIGKILL)
|
|
process.Wait()
|
|
if tty != nil {
|
|
tty.Close()
|
|
}
|
|
return -1, err
|
|
}
|
|
}
|
|
if detach {
|
|
return 0, nil
|
|
}
|
|
|
|
return handler.forward(process)
|
|
}
|