Remove nsinit
Signed-off-by: Michael Crosby <crosbymichael@gmail.com>
This commit is contained in:
parent
b675770455
commit
4b5bf88a08
|
@ -1,2 +0,0 @@
|
||||||
all:
|
|
||||||
go build -o nsinit .
|
|
128
nsinit/README.md
128
nsinit/README.md
|
@ -1,128 +0,0 @@
|
||||||
## nsinit
|
|
||||||
|
|
||||||
`nsinit` is a cli application which demonstrates the use of libcontainer.
|
|
||||||
It is able to spawn new containers or join existing containers.
|
|
||||||
|
|
||||||
### How to build?
|
|
||||||
|
|
||||||
First add the `libcontainer/vendor` into your GOPATH. It's because libcontainer
|
|
||||||
vendors all its dependencies, so it can be built predictably.
|
|
||||||
|
|
||||||
```
|
|
||||||
export GOPATH=$GOPATH:/your/path/to/libcontainer/vendor
|
|
||||||
```
|
|
||||||
|
|
||||||
Then get into the nsinit folder and get the imported file. Use `make` command
|
|
||||||
to make the nsinit binary.
|
|
||||||
|
|
||||||
```
|
|
||||||
cd libcontainer/nsinit
|
|
||||||
go get
|
|
||||||
make
|
|
||||||
```
|
|
||||||
|
|
||||||
We have finished compiling the nsinit package, but a root filesystem must be
|
|
||||||
provided for use along with a container configuration file.
|
|
||||||
|
|
||||||
Choose a proper place to run your container. For example we use `/busybox`.
|
|
||||||
|
|
||||||
```
|
|
||||||
mkdir /busybox
|
|
||||||
curl -sSL 'https://github.com/jpetazzo/docker-busybox/raw/buildroot-2014.11/rootfs.tar' | tar -xC /busybox
|
|
||||||
```
|
|
||||||
|
|
||||||
Then you may need to write a configuration file named `container.json` in the
|
|
||||||
`/busybox` folder. Environment, networking, and different capabilities for
|
|
||||||
the container are specified in this file. The configuration is used for each
|
|
||||||
process executed inside the container.
|
|
||||||
|
|
||||||
See the `sample_configs` folder for examples of what the container configuration
|
|
||||||
should look like.
|
|
||||||
|
|
||||||
```
|
|
||||||
cp libcontainer/sample_configs/minimal.json /busybox/container.json
|
|
||||||
cd /busybox
|
|
||||||
```
|
|
||||||
|
|
||||||
You can customize `container.json` per your needs. After that, nsinit is
|
|
||||||
ready to work.
|
|
||||||
|
|
||||||
To execute `/bin/bash` in the current directory as a container just run the
|
|
||||||
following **as root**:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
nsinit exec --tty --config container.json /bin/bash
|
|
||||||
```
|
|
||||||
|
|
||||||
If you wish to spawn another process inside the container while your current
|
|
||||||
bash session is running, run the same command again to get another bash shell
|
|
||||||
(or change the command). If the original process (PID 1) dies, all other
|
|
||||||
processes spawned inside the container will be killed and the namespace will
|
|
||||||
be removed.
|
|
||||||
|
|
||||||
You can identify if a process is running in a container by looking to see if
|
|
||||||
`state.json` is in the root of the directory.
|
|
||||||
|
|
||||||
You may also specify an alternate root directory from where the `container.json`
|
|
||||||
file is read and where the `state.json` file will be saved.
|
|
||||||
|
|
||||||
### How to use?
|
|
||||||
|
|
||||||
Currently nsinit has 9 commands. Type `nsinit -h` to list all of them.
|
|
||||||
And for every alternative command, you can also use `--help` to get more
|
|
||||||
detailed help documents. For example, `nsinit config --help`.
|
|
||||||
|
|
||||||
`nsinit` cli application is implemented using [cli.go](https://github.com/codegangsta/cli).
|
|
||||||
Lots of details are handled in cli.go, so the implementation of `nsinit` itself
|
|
||||||
is very clean and clear.
|
|
||||||
|
|
||||||
* **config**
|
|
||||||
It will generate a standard configuration file for a container. By default, it
|
|
||||||
will generate as the template file in [config.go](https://github.com/docker/libcontainer/blob/f28dff5539855bac2adbc5699f57f84349605b5f/nsinit/config.go#L234).
|
|
||||||
It will modify the template if you have specified some configuration by options.
|
|
||||||
* **exec**
|
|
||||||
Starts a container and execute a new command inside it. Besides common options, it
|
|
||||||
has some special options as below.
|
|
||||||
- `--tty,-t`: allocate a TTY to the container.
|
|
||||||
- `--config`: you can specify a configuration file. By default, it will use
|
|
||||||
template configuration.
|
|
||||||
- `--id`: specify the ID for a container. By default, the id is "nsinit".
|
|
||||||
- `--user,-u`: set the user, uid, and/or gid for the process. By default the
|
|
||||||
value is "root".
|
|
||||||
- `--cwd`: set the current working dir.
|
|
||||||
- `--env`: set environment variables for the process.
|
|
||||||
* **init**
|
|
||||||
It's an internal command that is called inside the container's namespaces to
|
|
||||||
initialize the namespace and exec the user's process. It should not be called
|
|
||||||
externally.
|
|
||||||
* **oom**
|
|
||||||
Display oom notifications for a container, you should specify container id.
|
|
||||||
* **pause**
|
|
||||||
Pause the container's processes, you should specify container id. It will use
|
|
||||||
cgroup freeze subsystem to help.
|
|
||||||
* **unpause**
|
|
||||||
Unpause the container's processes. Same with `pause`.
|
|
||||||
* **stats**
|
|
||||||
Display statistics for the container, it will mainly show cgroup and network
|
|
||||||
statistics.
|
|
||||||
* **state**
|
|
||||||
Get the container's current state. You can also read the state from `state.json`
|
|
||||||
in your container_id folder.
|
|
||||||
* **checkpoint**
|
|
||||||
Checkpoint a running container. You can read [this](http://criu.org/Advanced_usage)
|
|
||||||
for more detailed information about options.
|
|
||||||
- `--id`: specify the ID for a container. By default, the id is "nsinit".
|
|
||||||
- `--image-path`: path for saving criu image files. You must specify this option.
|
|
||||||
- `--work-path`: path for saving work files and logs. By default it will
|
|
||||||
generate a folder named "criu.work" in root directory.
|
|
||||||
- `--leave-running`: leave the process running after checkpointing.
|
|
||||||
- `--tcp-established`: allow open tcp connections.
|
|
||||||
- `--ext-unix-sk`: allow external unix sockets.
|
|
||||||
- `--shell-job`: allow shell jobs.
|
|
||||||
- `--page-server`: ADDRESS:PORT of the page server. The dump image can be
|
|
||||||
sent to a criu page server if we have a page server.
|
|
||||||
* **restore**
|
|
||||||
Restore a container from a previous checkpoint. Options are almost the same
|
|
||||||
with checkpoint and `--image-path` must be specified.
|
|
||||||
* **help, h**
|
|
||||||
Shows a list of commands or help for one command.
|
|
|
@ -1,67 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"github.com/codegangsta/cli"
|
|
||||||
"github.com/docker/libcontainer"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
var checkpointCommand = cli.Command{
|
|
||||||
Name: "checkpoint",
|
|
||||||
Usage: "checkpoint a running container",
|
|
||||||
Flags: []cli.Flag{
|
|
||||||
cli.StringFlag{Name: "id", Value: "nsinit", Usage: "specify the ID for a container"},
|
|
||||||
cli.StringFlag{Name: "image-path", Value: "", Usage: "path for saving criu image files"},
|
|
||||||
cli.StringFlag{Name: "work-path", Value: "", Usage: "path for saving work files and logs"},
|
|
||||||
cli.BoolFlag{Name: "leave-running", Usage: "leave the process running after checkpointing"},
|
|
||||||
cli.BoolFlag{Name: "tcp-established", Usage: "allow open tcp connections"},
|
|
||||||
cli.BoolFlag{Name: "ext-unix-sk", Usage: "allow external unix sockets"},
|
|
||||||
cli.BoolFlag{Name: "shell-job", Usage: "allow shell jobs"},
|
|
||||||
cli.StringFlag{Name: "page-server", Value: "", Usage: "ADDRESS:PORT of the page server"},
|
|
||||||
},
|
|
||||||
Action: func(context *cli.Context) {
|
|
||||||
imagePath := context.String("image-path")
|
|
||||||
if imagePath == "" {
|
|
||||||
fatal(fmt.Errorf("The --image-path option isn't specified"))
|
|
||||||
}
|
|
||||||
|
|
||||||
container, err := getContainer(context)
|
|
||||||
if err != nil {
|
|
||||||
fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// these are the mandatory criu options for a container
|
|
||||||
criuOpts := &libcontainer.CriuOpts{
|
|
||||||
ImagesDirectory: imagePath,
|
|
||||||
WorkDirectory: context.String("work-path"),
|
|
||||||
LeaveRunning: context.Bool("leave-running"),
|
|
||||||
TcpEstablished: context.Bool("tcp-established"),
|
|
||||||
ExternalUnixConnections: context.Bool("ext-unix-sk"),
|
|
||||||
ShellJob: context.Bool("shell-job"),
|
|
||||||
}
|
|
||||||
|
|
||||||
// xxx following criu opts are optional
|
|
||||||
// The dump image can be sent to a criu page server
|
|
||||||
if psOpt := context.String("page-server"); psOpt != "" {
|
|
||||||
addressPort := strings.Split(psOpt, ":")
|
|
||||||
if len(addressPort) != 2 {
|
|
||||||
fatal(fmt.Errorf("Use --page-server ADDRESS:PORT to specify page server"))
|
|
||||||
}
|
|
||||||
|
|
||||||
port_int, err := strconv.Atoi(addressPort[1])
|
|
||||||
if err != nil {
|
|
||||||
fatal(fmt.Errorf("Invalid port number"))
|
|
||||||
}
|
|
||||||
criuOpts.PageServer = libcontainer.CriuPageServerInfo{
|
|
||||||
Address: addressPort[0],
|
|
||||||
Port: int32(port_int),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := container.Checkpoint(criuOpts); err != nil {
|
|
||||||
fatal(err)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
323
nsinit/config.go
323
nsinit/config.go
|
@ -1,323 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"encoding/json"
|
|
||||||
"io"
|
|
||||||
"math"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"strings"
|
|
||||||
"syscall"
|
|
||||||
|
|
||||||
"github.com/Sirupsen/logrus"
|
|
||||||
"github.com/codegangsta/cli"
|
|
||||||
"github.com/docker/libcontainer/configs"
|
|
||||||
"github.com/docker/libcontainer/utils"
|
|
||||||
)
|
|
||||||
|
|
||||||
const defaultMountFlags = syscall.MS_NOEXEC | syscall.MS_NOSUID | syscall.MS_NODEV
|
|
||||||
|
|
||||||
var createFlags = []cli.Flag{
|
|
||||||
cli.BoolFlag{Name: "cgroup", Usage: "mount the cgroup data for the container"},
|
|
||||||
cli.BoolFlag{Name: "read-only", Usage: "set the container's rootfs as read-only"},
|
|
||||||
cli.IntFlag{Name: "cpushares", Usage: "set the cpushares for the container"},
|
|
||||||
cli.IntFlag{Name: "memory-limit", Usage: "set the memory limit for the container"},
|
|
||||||
cli.IntFlag{Name: "memory-swap", Usage: "set the memory swap limit for the container"},
|
|
||||||
cli.IntFlag{Name: "memory-swappiness", Usage: "set the memory swappiness value (0-100 inclusive) for the container"},
|
|
||||||
cli.IntFlag{Name: "parent-death-signal", Usage: "set the signal that will be delivered to the process in case the parent dies"},
|
|
||||||
cli.IntFlag{Name: "userns-root-uid", Usage: "set the user namespace root uid"},
|
|
||||||
cli.IntFlag{Name: "veth-mtu", Usage: "veth mtu"},
|
|
||||||
cli.StringFlag{Name: "apparmor-profile", Usage: "set the apparmor profile"},
|
|
||||||
cli.StringFlag{Name: "cpuset-cpus", Usage: "set the cpuset cpus"},
|
|
||||||
cli.StringFlag{Name: "cpuset-mems", Usage: "set the cpuset mems"},
|
|
||||||
cli.StringFlag{Name: "hostname", Value: "nsinit", Usage: "hostname value for the container"},
|
|
||||||
cli.StringFlag{Name: "ipc", Value: "", Usage: "ipc namespace"},
|
|
||||||
cli.StringFlag{Name: "mnt", Value: "", Usage: "mount namespace"},
|
|
||||||
cli.StringFlag{Name: "mount-label", Usage: "set the mount label"},
|
|
||||||
cli.StringFlag{Name: "net", Value: "", Usage: "network namespace"},
|
|
||||||
cli.StringFlag{Name: "pid", Value: "", Usage: "pid namespace"},
|
|
||||||
cli.StringFlag{Name: "process-label", Usage: "set the process label"},
|
|
||||||
cli.StringFlag{Name: "rootfs", Usage: "set the rootfs"},
|
|
||||||
cli.StringFlag{Name: "security", Value: "", Usage: "set the security profile (high, medium, low)"},
|
|
||||||
cli.StringFlag{Name: "uts", Value: "", Usage: "uts namespace"},
|
|
||||||
cli.StringFlag{Name: "veth-address", Usage: "veth ip address"},
|
|
||||||
cli.StringFlag{Name: "veth-bridge", Usage: "veth bridge"},
|
|
||||||
cli.StringFlag{Name: "veth-gateway", Usage: "veth gateway address"},
|
|
||||||
cli.StringSliceFlag{Name: "bind", Value: &cli.StringSlice{}, Usage: "add bind mounts to the container"},
|
|
||||||
cli.StringSliceFlag{Name: "sysctl", Value: &cli.StringSlice{}, Usage: "set system properties in the container"},
|
|
||||||
cli.StringSliceFlag{Name: "tmpfs", Value: &cli.StringSlice{}, Usage: "add tmpfs mounts to the container"},
|
|
||||||
cli.StringSliceFlag{Name: "groups", Value: &cli.StringSlice{}, Usage: "add additional groups"},
|
|
||||||
}
|
|
||||||
|
|
||||||
var configCommand = cli.Command{
|
|
||||||
Name: "config",
|
|
||||||
Usage: "generate a standard configuration file for a container",
|
|
||||||
Flags: append([]cli.Flag{
|
|
||||||
cli.StringFlag{Name: "file,f", Value: "stdout", Usage: "write the configuration to the specified file"},
|
|
||||||
}, createFlags...),
|
|
||||||
Action: func(context *cli.Context) {
|
|
||||||
template := getTemplate()
|
|
||||||
modify(template, context)
|
|
||||||
data, err := json.MarshalIndent(template, "", "\t")
|
|
||||||
if err != nil {
|
|
||||||
fatal(err)
|
|
||||||
}
|
|
||||||
var f *os.File
|
|
||||||
filePath := context.String("file")
|
|
||||||
switch filePath {
|
|
||||||
case "stdout", "":
|
|
||||||
f = os.Stdout
|
|
||||||
default:
|
|
||||||
if f, err = os.Create(filePath); err != nil {
|
|
||||||
fatal(err)
|
|
||||||
}
|
|
||||||
defer f.Close()
|
|
||||||
}
|
|
||||||
if _, err := io.Copy(f, bytes.NewBuffer(data)); err != nil {
|
|
||||||
fatal(err)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
func modify(config *configs.Config, context *cli.Context) {
|
|
||||||
config.ParentDeathSignal = context.Int("parent-death-signal")
|
|
||||||
config.Readonlyfs = context.Bool("read-only")
|
|
||||||
config.Cgroups.CpusetCpus = context.String("cpuset-cpus")
|
|
||||||
config.Cgroups.CpusetMems = context.String("cpuset-mems")
|
|
||||||
config.Cgroups.CpuShares = int64(context.Int("cpushares"))
|
|
||||||
config.Cgroups.Memory = int64(context.Int("memory-limit"))
|
|
||||||
config.Cgroups.MemorySwap = int64(context.Int("memory-swap"))
|
|
||||||
config.Cgroups.MemorySwappiness = int64(context.Int("memory-swappiness"))
|
|
||||||
config.AppArmorProfile = context.String("apparmor-profile")
|
|
||||||
config.ProcessLabel = context.String("process-label")
|
|
||||||
config.MountLabel = context.String("mount-label")
|
|
||||||
|
|
||||||
rootfs := context.String("rootfs")
|
|
||||||
if rootfs != "" {
|
|
||||||
config.Rootfs = rootfs
|
|
||||||
}
|
|
||||||
|
|
||||||
userns_uid := context.Int("userns-root-uid")
|
|
||||||
if userns_uid != 0 {
|
|
||||||
config.Namespaces.Add(configs.NEWUSER, "")
|
|
||||||
config.UidMappings = []configs.IDMap{
|
|
||||||
{ContainerID: 0, HostID: userns_uid, Size: 1},
|
|
||||||
{ContainerID: 1, HostID: 1, Size: userns_uid - 1},
|
|
||||||
{ContainerID: userns_uid + 1, HostID: userns_uid + 1, Size: math.MaxInt32 - userns_uid},
|
|
||||||
}
|
|
||||||
config.GidMappings = []configs.IDMap{
|
|
||||||
{ContainerID: 0, HostID: userns_uid, Size: 1},
|
|
||||||
{ContainerID: 1, HostID: 1, Size: userns_uid - 1},
|
|
||||||
{ContainerID: userns_uid + 1, HostID: userns_uid + 1, Size: math.MaxInt32 - userns_uid},
|
|
||||||
}
|
|
||||||
for _, node := range config.Devices {
|
|
||||||
node.Uid = uint32(userns_uid)
|
|
||||||
node.Gid = uint32(userns_uid)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
config.SystemProperties = make(map[string]string)
|
|
||||||
for _, sysProp := range context.StringSlice("sysctl") {
|
|
||||||
parts := strings.SplitN(sysProp, "=", 2)
|
|
||||||
if len(parts) != 2 {
|
|
||||||
logrus.Fatalf("invalid system property %s", sysProp)
|
|
||||||
}
|
|
||||||
config.SystemProperties[parts[0]] = parts[1]
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, group := range context.StringSlice("groups") {
|
|
||||||
config.AdditionalGroups = append(config.AdditionalGroups, group)
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, rawBind := range context.StringSlice("bind") {
|
|
||||||
mount := &configs.Mount{
|
|
||||||
Device: "bind",
|
|
||||||
Flags: syscall.MS_BIND | syscall.MS_REC,
|
|
||||||
}
|
|
||||||
parts := strings.SplitN(rawBind, ":", 3)
|
|
||||||
switch len(parts) {
|
|
||||||
default:
|
|
||||||
logrus.Fatalf("invalid bind mount %s", rawBind)
|
|
||||||
case 2:
|
|
||||||
mount.Source, mount.Destination = parts[0], parts[1]
|
|
||||||
case 3:
|
|
||||||
mount.Source, mount.Destination = parts[0], parts[1]
|
|
||||||
switch parts[2] {
|
|
||||||
case "ro":
|
|
||||||
mount.Flags |= syscall.MS_RDONLY
|
|
||||||
case "rw":
|
|
||||||
default:
|
|
||||||
logrus.Fatalf("invalid bind mount mode %s", parts[2])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
config.Mounts = append(config.Mounts, mount)
|
|
||||||
}
|
|
||||||
for _, tmpfs := range context.StringSlice("tmpfs") {
|
|
||||||
config.Mounts = append(config.Mounts, &configs.Mount{
|
|
||||||
Device: "tmpfs",
|
|
||||||
Destination: tmpfs,
|
|
||||||
Flags: syscall.MS_NOEXEC | syscall.MS_NOSUID | syscall.MS_NODEV,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
for flag, value := range map[string]configs.NamespaceType{
|
|
||||||
"net": configs.NEWNET,
|
|
||||||
"mnt": configs.NEWNS,
|
|
||||||
"pid": configs.NEWPID,
|
|
||||||
"ipc": configs.NEWIPC,
|
|
||||||
"uts": configs.NEWUTS,
|
|
||||||
} {
|
|
||||||
switch v := context.String(flag); v {
|
|
||||||
case "host":
|
|
||||||
config.Namespaces.Remove(value)
|
|
||||||
case "", "private":
|
|
||||||
if !config.Namespaces.Contains(value) {
|
|
||||||
config.Namespaces.Add(value, "")
|
|
||||||
}
|
|
||||||
if flag == "net" {
|
|
||||||
config.Networks = []*configs.Network{
|
|
||||||
{
|
|
||||||
Type: "loopback",
|
|
||||||
Address: "127.0.0.1/0",
|
|
||||||
Gateway: "localhost",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if flag == "uts" {
|
|
||||||
config.Hostname = context.String("hostname")
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
config.Namespaces.Remove(value)
|
|
||||||
config.Namespaces.Add(value, v)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if bridge := context.String("veth-bridge"); bridge != "" {
|
|
||||||
hostName, err := utils.GenerateRandomName("veth", 7)
|
|
||||||
if err != nil {
|
|
||||||
logrus.Fatal(err)
|
|
||||||
}
|
|
||||||
network := &configs.Network{
|
|
||||||
Type: "veth",
|
|
||||||
Name: "eth0",
|
|
||||||
Bridge: bridge,
|
|
||||||
Address: context.String("veth-address"),
|
|
||||||
Gateway: context.String("veth-gateway"),
|
|
||||||
Mtu: context.Int("veth-mtu"),
|
|
||||||
HostInterfaceName: hostName,
|
|
||||||
}
|
|
||||||
config.Networks = append(config.Networks, network)
|
|
||||||
}
|
|
||||||
if context.Bool("cgroup") {
|
|
||||||
config.Mounts = append(config.Mounts, &configs.Mount{
|
|
||||||
Destination: "/sys/fs/cgroup",
|
|
||||||
Device: "cgroup",
|
|
||||||
})
|
|
||||||
}
|
|
||||||
modifySecurityProfile(context, config)
|
|
||||||
}
|
|
||||||
|
|
||||||
func modifySecurityProfile(context *cli.Context, config *configs.Config) {
|
|
||||||
profileName := context.String("security")
|
|
||||||
if profileName == "" {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
profile := profiles[profileName]
|
|
||||||
if profile == nil {
|
|
||||||
logrus.Fatalf("invalid profile name %q", profileName)
|
|
||||||
}
|
|
||||||
config.Rlimits = profile.Rlimits
|
|
||||||
config.Capabilities = profile.Capabilities
|
|
||||||
config.Seccomp = profile.Seccomp
|
|
||||||
config.AppArmorProfile = profile.ApparmorProfile
|
|
||||||
config.MountLabel = profile.MountLabel
|
|
||||||
config.ProcessLabel = profile.ProcessLabel
|
|
||||||
}
|
|
||||||
|
|
||||||
func getTemplate() *configs.Config {
|
|
||||||
cwd, err := os.Getwd()
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
return &configs.Config{
|
|
||||||
Rootfs: cwd,
|
|
||||||
ParentDeathSignal: int(syscall.SIGKILL),
|
|
||||||
Capabilities: []string{
|
|
||||||
"CHOWN",
|
|
||||||
"DAC_OVERRIDE",
|
|
||||||
"FSETID",
|
|
||||||
"FOWNER",
|
|
||||||
"MKNOD",
|
|
||||||
"NET_RAW",
|
|
||||||
"SETGID",
|
|
||||||
"SETUID",
|
|
||||||
"SETFCAP",
|
|
||||||
"SETPCAP",
|
|
||||||
"NET_BIND_SERVICE",
|
|
||||||
"SYS_CHROOT",
|
|
||||||
"KILL",
|
|
||||||
"AUDIT_WRITE",
|
|
||||||
},
|
|
||||||
Namespaces: configs.Namespaces([]configs.Namespace{
|
|
||||||
{Type: configs.NEWNS},
|
|
||||||
{Type: configs.NEWUTS},
|
|
||||||
{Type: configs.NEWIPC},
|
|
||||||
{Type: configs.NEWPID},
|
|
||||||
{Type: configs.NEWNET},
|
|
||||||
}),
|
|
||||||
Cgroups: &configs.Cgroup{
|
|
||||||
Name: filepath.Base(cwd),
|
|
||||||
Parent: "nsinit",
|
|
||||||
AllowAllDevices: false,
|
|
||||||
AllowedDevices: configs.DefaultAllowedDevices,
|
|
||||||
MemorySwappiness: -1,
|
|
||||||
},
|
|
||||||
Devices: configs.DefaultAutoCreatedDevices,
|
|
||||||
MaskPaths: []string{
|
|
||||||
"/proc/kcore",
|
|
||||||
},
|
|
||||||
ReadonlyPaths: []string{
|
|
||||||
"/proc/sys", "/proc/sysrq-trigger", "/proc/irq", "/proc/bus",
|
|
||||||
},
|
|
||||||
Mounts: []*configs.Mount{
|
|
||||||
{
|
|
||||||
Source: "proc",
|
|
||||||
Destination: "/proc",
|
|
||||||
Device: "proc",
|
|
||||||
Flags: defaultMountFlags,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Source: "tmpfs",
|
|
||||||
Destination: "/dev",
|
|
||||||
Device: "tmpfs",
|
|
||||||
Flags: syscall.MS_NOSUID | syscall.MS_STRICTATIME,
|
|
||||||
Data: "mode=755",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Source: "devpts",
|
|
||||||
Destination: "/dev/pts",
|
|
||||||
Device: "devpts",
|
|
||||||
Flags: syscall.MS_NOSUID | syscall.MS_NOEXEC,
|
|
||||||
Data: "newinstance,ptmxmode=0666,mode=0620,gid=5",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Device: "tmpfs",
|
|
||||||
Source: "shm",
|
|
||||||
Destination: "/dev/shm",
|
|
||||||
Data: "mode=1777,size=65536k",
|
|
||||||
Flags: defaultMountFlags,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Source: "mqueue",
|
|
||||||
Destination: "/dev/mqueue",
|
|
||||||
Device: "mqueue",
|
|
||||||
Flags: defaultMountFlags,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Source: "sysfs",
|
|
||||||
Destination: "/sys",
|
|
||||||
Device: "sysfs",
|
|
||||||
Flags: defaultMountFlags | syscall.MS_RDONLY,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
124
nsinit/exec.go
124
nsinit/exec.go
|
@ -1,124 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"os"
|
|
||||||
"os/exec"
|
|
||||||
"os/signal"
|
|
||||||
"syscall"
|
|
||||||
|
|
||||||
"github.com/codegangsta/cli"
|
|
||||||
"github.com/docker/libcontainer"
|
|
||||||
"github.com/docker/libcontainer/utils"
|
|
||||||
)
|
|
||||||
|
|
||||||
var standardEnvironment = &cli.StringSlice{
|
|
||||||
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
|
|
||||||
"HOSTNAME=nsinit",
|
|
||||||
"TERM=xterm",
|
|
||||||
}
|
|
||||||
|
|
||||||
var execCommand = cli.Command{
|
|
||||||
Name: "exec",
|
|
||||||
Usage: "execute a new command inside a container",
|
|
||||||
Action: execAction,
|
|
||||||
Flags: append([]cli.Flag{
|
|
||||||
cli.BoolFlag{Name: "tty,t", Usage: "allocate a TTY to the container"},
|
|
||||||
cli.BoolFlag{Name: "systemd", Usage: "Use systemd for managing cgroups, if available"},
|
|
||||||
cli.StringFlag{Name: "id", Value: "nsinit", Usage: "specify the ID for a container"},
|
|
||||||
cli.StringFlag{Name: "config", Value: "", Usage: "path to the configuration file"},
|
|
||||||
cli.StringFlag{Name: "user,u", Value: "root", Usage: "set the user, uid, and/or gid for the process"},
|
|
||||||
cli.StringFlag{Name: "cwd", Value: "", Usage: "set the current working dir"},
|
|
||||||
cli.StringSliceFlag{Name: "env", Value: standardEnvironment, Usage: "set environment variables for the process"},
|
|
||||||
}, createFlags...),
|
|
||||||
}
|
|
||||||
|
|
||||||
func execAction(context *cli.Context) {
|
|
||||||
factory, err := loadFactory(context)
|
|
||||||
if err != nil {
|
|
||||||
fatal(err)
|
|
||||||
}
|
|
||||||
config, err := loadConfig(context)
|
|
||||||
if err != nil {
|
|
||||||
fatal(err)
|
|
||||||
}
|
|
||||||
created := false
|
|
||||||
container, err := factory.Load(context.String("id"))
|
|
||||||
if err != nil {
|
|
||||||
created = true
|
|
||||||
if container, err = factory.Create(context.String("id"), config); err != nil {
|
|
||||||
fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
process := &libcontainer.Process{
|
|
||||||
Args: context.Args(),
|
|
||||||
Env: context.StringSlice("env"),
|
|
||||||
User: context.String("user"),
|
|
||||||
Cwd: context.String("cwd"),
|
|
||||||
Stdin: os.Stdin,
|
|
||||||
Stdout: os.Stdout,
|
|
||||||
Stderr: os.Stderr,
|
|
||||||
}
|
|
||||||
rootuid, err := config.HostUID()
|
|
||||||
if err != nil {
|
|
||||||
fatal(err)
|
|
||||||
}
|
|
||||||
tty, err := newTty(context, process, rootuid)
|
|
||||||
if err != nil {
|
|
||||||
fatal(err)
|
|
||||||
}
|
|
||||||
if err := tty.attach(process); err != nil {
|
|
||||||
fatal(err)
|
|
||||||
}
|
|
||||||
go handleSignals(process, tty)
|
|
||||||
err = container.Start(process)
|
|
||||||
if err != nil {
|
|
||||||
tty.Close()
|
|
||||||
if created {
|
|
||||||
container.Destroy()
|
|
||||||
}
|
|
||||||
fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
status, err := process.Wait()
|
|
||||||
if err != nil {
|
|
||||||
exitError, ok := err.(*exec.ExitError)
|
|
||||||
if ok {
|
|
||||||
status = exitError.ProcessState
|
|
||||||
} else {
|
|
||||||
tty.Close()
|
|
||||||
if created {
|
|
||||||
container.Destroy()
|
|
||||||
}
|
|
||||||
fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if created {
|
|
||||||
status, err := container.Status()
|
|
||||||
if err != nil {
|
|
||||||
tty.Close()
|
|
||||||
fatal(err)
|
|
||||||
}
|
|
||||||
if status != libcontainer.Checkpointed {
|
|
||||||
if err := container.Destroy(); err != nil {
|
|
||||||
tty.Close()
|
|
||||||
fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
tty.Close()
|
|
||||||
os.Exit(utils.ExitStatus(status.Sys().(syscall.WaitStatus)))
|
|
||||||
}
|
|
||||||
|
|
||||||
func handleSignals(container *libcontainer.Process, tty *tty) {
|
|
||||||
sigc := make(chan os.Signal, 10)
|
|
||||||
signal.Notify(sigc)
|
|
||||||
tty.resize()
|
|
||||||
for sig := range sigc {
|
|
||||||
switch sig {
|
|
||||||
case syscall.SIGWINCH:
|
|
||||||
tty.resize()
|
|
||||||
default:
|
|
||||||
container.Signal(sig)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,28 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"runtime"
|
|
||||||
|
|
||||||
"github.com/Sirupsen/logrus"
|
|
||||||
"github.com/codegangsta/cli"
|
|
||||||
"github.com/docker/libcontainer"
|
|
||||||
_ "github.com/docker/libcontainer/nsenter"
|
|
||||||
)
|
|
||||||
|
|
||||||
var initCommand = cli.Command{
|
|
||||||
Name: "init",
|
|
||||||
Usage: "runs the init process inside the namespace",
|
|
||||||
Action: func(context *cli.Context) {
|
|
||||||
logrus.SetLevel(logrus.DebugLevel)
|
|
||||||
runtime.GOMAXPROCS(1)
|
|
||||||
runtime.LockOSThread()
|
|
||||||
factory, err := libcontainer.New("")
|
|
||||||
if err != nil {
|
|
||||||
fatal(err)
|
|
||||||
}
|
|
||||||
if err := factory.StartInitialization(); err != nil {
|
|
||||||
fatal(err)
|
|
||||||
}
|
|
||||||
panic("This line should never been executed")
|
|
||||||
},
|
|
||||||
}
|
|
|
@ -1,49 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"os"
|
|
||||||
|
|
||||||
"github.com/Sirupsen/logrus"
|
|
||||||
"github.com/codegangsta/cli"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
app := cli.NewApp()
|
|
||||||
app.Name = "nsinit"
|
|
||||||
app.Version = "2"
|
|
||||||
app.Author = "libcontainer maintainers"
|
|
||||||
app.Flags = []cli.Flag{
|
|
||||||
cli.BoolFlag{Name: "debug", Usage: "enable debug output in the logs"},
|
|
||||||
cli.StringFlag{Name: "root", Value: "/var/run/nsinit", Usage: "root directory for containers"},
|
|
||||||
cli.StringFlag{Name: "log-file", Usage: "set the log file to output logs to"},
|
|
||||||
cli.StringFlag{Name: "criu", Value: "criu", Usage: "path to the criu binary for checkpoint and restore"},
|
|
||||||
}
|
|
||||||
app.Commands = []cli.Command{
|
|
||||||
checkpointCommand,
|
|
||||||
configCommand,
|
|
||||||
execCommand,
|
|
||||||
initCommand,
|
|
||||||
oomCommand,
|
|
||||||
pauseCommand,
|
|
||||||
stateCommand,
|
|
||||||
statsCommand,
|
|
||||||
unpauseCommand,
|
|
||||||
restoreCommand,
|
|
||||||
}
|
|
||||||
app.Before = func(context *cli.Context) error {
|
|
||||||
if context.GlobalBool("debug") {
|
|
||||||
logrus.SetLevel(logrus.DebugLevel)
|
|
||||||
}
|
|
||||||
if path := context.GlobalString("log-file"); path != "" {
|
|
||||||
f, err := os.Create(path)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
logrus.SetOutput(f)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
if err := app.Run(os.Args); err != nil {
|
|
||||||
logrus.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,29 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/Sirupsen/logrus"
|
|
||||||
"github.com/codegangsta/cli"
|
|
||||||
)
|
|
||||||
|
|
||||||
var oomCommand = cli.Command{
|
|
||||||
Name: "oom",
|
|
||||||
Usage: "display oom notifications for a container",
|
|
||||||
Flags: []cli.Flag{
|
|
||||||
cli.StringFlag{Name: "id", Value: "nsinit", Usage: "specify the ID for a container"},
|
|
||||||
},
|
|
||||||
Action: func(context *cli.Context) {
|
|
||||||
container, err := getContainer(context)
|
|
||||||
if err != nil {
|
|
||||||
logrus.Fatal(err)
|
|
||||||
}
|
|
||||||
n, err := container.NotifyOOM()
|
|
||||||
if err != nil {
|
|
||||||
logrus.Fatal(err)
|
|
||||||
}
|
|
||||||
for x := range n {
|
|
||||||
// hack for calm down go1.4 gofmt
|
|
||||||
_ = x
|
|
||||||
logrus.Printf("OOM notification received")
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
|
@ -1,40 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/Sirupsen/logrus"
|
|
||||||
"github.com/codegangsta/cli"
|
|
||||||
)
|
|
||||||
|
|
||||||
var pauseCommand = cli.Command{
|
|
||||||
Name: "pause",
|
|
||||||
Usage: "pause the container's processes",
|
|
||||||
Flags: []cli.Flag{
|
|
||||||
cli.StringFlag{Name: "id", Value: "nsinit", Usage: "specify the ID for a container"},
|
|
||||||
},
|
|
||||||
Action: func(context *cli.Context) {
|
|
||||||
container, err := getContainer(context)
|
|
||||||
if err != nil {
|
|
||||||
logrus.Fatal(err)
|
|
||||||
}
|
|
||||||
if err = container.Pause(); err != nil {
|
|
||||||
logrus.Fatal(err)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
var unpauseCommand = cli.Command{
|
|
||||||
Name: "unpause",
|
|
||||||
Usage: "unpause the container's processes",
|
|
||||||
Flags: []cli.Flag{
|
|
||||||
cli.StringFlag{Name: "id", Value: "nsinit", Usage: "specify the ID for a container"},
|
|
||||||
},
|
|
||||||
Action: func(context *cli.Context) {
|
|
||||||
container, err := getContainer(context)
|
|
||||||
if err != nil {
|
|
||||||
logrus.Fatal(err)
|
|
||||||
}
|
|
||||||
if err = container.Resume(); err != nil {
|
|
||||||
logrus.Fatal(err)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
|
@ -1,120 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
"os/exec"
|
|
||||||
"syscall"
|
|
||||||
|
|
||||||
"github.com/codegangsta/cli"
|
|
||||||
"github.com/docker/libcontainer"
|
|
||||||
"github.com/docker/libcontainer/utils"
|
|
||||||
)
|
|
||||||
|
|
||||||
var restoreCommand = cli.Command{
|
|
||||||
Name: "restore",
|
|
||||||
Usage: "restore a container from a previous checkpoint",
|
|
||||||
Flags: []cli.Flag{
|
|
||||||
cli.StringFlag{Name: "id", Value: "nsinit", Usage: "specify the ID for a container"},
|
|
||||||
cli.StringFlag{Name: "image-path", Value: "", Usage: "path to criu image files for restoring"},
|
|
||||||
cli.StringFlag{Name: "work-path", Value: "", Usage: "path for saving work files and logs"},
|
|
||||||
cli.BoolFlag{Name: "tcp-established", Usage: "allow open tcp connections"},
|
|
||||||
cli.BoolFlag{Name: "ext-unix-sk", Usage: "allow external unix sockets"},
|
|
||||||
cli.BoolFlag{Name: "shell-job", Usage: "allow shell jobs"},
|
|
||||||
},
|
|
||||||
Action: func(context *cli.Context) {
|
|
||||||
imagePath := context.String("image-path")
|
|
||||||
if imagePath == "" {
|
|
||||||
fatal(fmt.Errorf("The --image-path option isn't specified"))
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
container libcontainer.Container
|
|
||||||
err error
|
|
||||||
)
|
|
||||||
|
|
||||||
factory, err := loadFactory(context)
|
|
||||||
if err != nil {
|
|
||||||
fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
config, err := loadConfig(context)
|
|
||||||
if err != nil {
|
|
||||||
fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
created := false
|
|
||||||
container, err = factory.Load(context.String("id"))
|
|
||||||
if err != nil {
|
|
||||||
created = true
|
|
||||||
if container, err = factory.Create(context.String("id"), config); err != nil {
|
|
||||||
fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
process := &libcontainer.Process{
|
|
||||||
Stdin: os.Stdin,
|
|
||||||
Stdout: os.Stdout,
|
|
||||||
Stderr: os.Stderr,
|
|
||||||
}
|
|
||||||
//rootuid, err := config.HostUID()
|
|
||||||
//if err != nil {
|
|
||||||
//fatal(err)
|
|
||||||
//}
|
|
||||||
rootuid := 0 // XXX
|
|
||||||
tty, err := newTty(context, process, rootuid)
|
|
||||||
if err != nil {
|
|
||||||
fatal(err)
|
|
||||||
}
|
|
||||||
if err := tty.attach(process); err != nil {
|
|
||||||
fatal(err)
|
|
||||||
}
|
|
||||||
go handleSignals(process, tty)
|
|
||||||
|
|
||||||
err = container.Restore(process, &libcontainer.CriuOpts{
|
|
||||||
ImagesDirectory: imagePath,
|
|
||||||
WorkDirectory: context.String("work-path"),
|
|
||||||
TcpEstablished: context.Bool("tcp-established"),
|
|
||||||
ExternalUnixConnections: context.Bool("ext-unix-sk"),
|
|
||||||
ShellJob: context.Bool("shell-job"),
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
tty.Close()
|
|
||||||
if created {
|
|
||||||
container.Destroy()
|
|
||||||
}
|
|
||||||
fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
status, err := process.Wait()
|
|
||||||
if err != nil {
|
|
||||||
exitError, ok := err.(*exec.ExitError)
|
|
||||||
if ok {
|
|
||||||
status = exitError.ProcessState
|
|
||||||
} else {
|
|
||||||
tty.Close()
|
|
||||||
if created {
|
|
||||||
container.Destroy()
|
|
||||||
}
|
|
||||||
fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if created {
|
|
||||||
status, err := container.Status()
|
|
||||||
if err != nil {
|
|
||||||
tty.Close()
|
|
||||||
fatal(err)
|
|
||||||
}
|
|
||||||
if status != libcontainer.Checkpointed {
|
|
||||||
if err := container.Destroy(); err != nil {
|
|
||||||
tty.Close()
|
|
||||||
fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
tty.Close()
|
|
||||||
os.Exit(utils.ExitStatus(status.Sys().(syscall.WaitStatus)))
|
|
||||||
},
|
|
||||||
}
|
|
|
@ -1,272 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"syscall"
|
|
||||||
|
|
||||||
"github.com/docker/libcontainer/configs"
|
|
||||||
"github.com/docker/libcontainer/system"
|
|
||||||
)
|
|
||||||
|
|
||||||
var profiles = map[string]*securityProfile{
|
|
||||||
"high": highProfile,
|
|
||||||
"medium": mediumProfile,
|
|
||||||
"low": lowProfile,
|
|
||||||
}
|
|
||||||
|
|
||||||
type securityProfile struct {
|
|
||||||
Capabilities []string `json:"capabilities"`
|
|
||||||
ApparmorProfile string `json:"apparmor_profile"`
|
|
||||||
MountLabel string `json:"mount_label"`
|
|
||||||
ProcessLabel string `json:"process_label"`
|
|
||||||
Rlimits []configs.Rlimit `json:"rlimits"`
|
|
||||||
Seccomp *configs.Seccomp `json:"seccomp"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// this should be a runtime config that is not able to do things like apt-get or yum install.
|
|
||||||
var highProfile = &securityProfile{
|
|
||||||
Capabilities: []string{
|
|
||||||
"NET_BIND_SERVICE",
|
|
||||||
"KILL",
|
|
||||||
"AUDIT_WRITE",
|
|
||||||
},
|
|
||||||
Rlimits: []configs.Rlimit{
|
|
||||||
{
|
|
||||||
Type: syscall.RLIMIT_NOFILE,
|
|
||||||
Hard: 1024,
|
|
||||||
Soft: 1024,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
// http://man7.org/linux/man-pages/man2/syscalls.2.html
|
|
||||||
Seccomp: &configs.Seccomp{
|
|
||||||
Syscalls: []*configs.Syscall{
|
|
||||||
{
|
|
||||||
Value: syscall.SYS_CAPSET, // http://man7.org/linux/man-pages/man2/capset.2.html
|
|
||||||
Action: configs.Action(syscall.EPERM),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Value: syscall.SYS_UNSHARE, // http://man7.org/linux/man-pages/man2/unshare.2.html
|
|
||||||
Action: configs.Action(syscall.EPERM),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Value: int(system.SysSetns()),
|
|
||||||
Action: configs.Action(syscall.EPERM),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Value: syscall.SYS_MOUNT, // http://man7.org/linux/man-pages/man2/mount.2.html
|
|
||||||
Action: configs.Action(syscall.EPERM),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Value: syscall.SYS_UMOUNT2, // http://man7.org/linux/man-pages/man2/umount.2.html
|
|
||||||
Action: configs.Action(syscall.EPERM),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Value: syscall.SYS_CREATE_MODULE, // http://man7.org/linux/man-pages/man2/create_module.2.html
|
|
||||||
Action: configs.Action(syscall.EPERM),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Value: syscall.SYS_DELETE_MODULE, // http://man7.org/linux/man-pages/man2/delete_module.2.html
|
|
||||||
Action: configs.Action(syscall.EPERM),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Value: syscall.SYS_CHMOD, // http://man7.org/linux/man-pages/man2/chmod.2.html
|
|
||||||
Action: configs.Action(syscall.EPERM),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Value: syscall.SYS_CHOWN, // http://man7.org/linux/man-pages/man2/chown.2.html
|
|
||||||
Action: configs.Action(syscall.EPERM),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Value: syscall.SYS_LINK, // http://man7.org/linux/man-pages/man2/link.2.html
|
|
||||||
Action: configs.Action(syscall.EPERM),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Value: syscall.SYS_LINKAT, // http://man7.org/linux/man-pages/man2/linkat.2.html
|
|
||||||
Action: configs.Action(syscall.EPERM),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Value: syscall.SYS_UNLINK, // http://man7.org/linux/man-pages/man2/unlink.2.html
|
|
||||||
Action: configs.Action(syscall.EPERM),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Value: syscall.SYS_UNLINKAT, // http://man7.org/linux/man-pages/man2/unlinkat.2.html
|
|
||||||
Action: configs.Action(syscall.EPERM),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Value: syscall.SYS_CHROOT, // http://man7.org/linux/man-pages/man2/chroot.2.html
|
|
||||||
Action: configs.Action(syscall.EPERM),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Value: syscall.SYS_KEXEC_LOAD, // http://man7.org/linux/man-pages/man2/kexec_load.2.html
|
|
||||||
Action: configs.Action(syscall.EPERM),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Value: syscall.SYS_SETDOMAINNAME, // http://man7.org/linux/man-pages/man2/setdomainname.2.html
|
|
||||||
Action: configs.Action(syscall.EPERM),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Value: syscall.SYS_SETHOSTNAME, // http://man7.org/linux/man-pages/man2/sethostname.2.html
|
|
||||||
Action: configs.Action(syscall.EPERM),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Value: syscall.SYS_CLONE, // http://man7.org/linux/man-pages/man2/clone.2.html
|
|
||||||
Action: configs.Action(syscall.EPERM),
|
|
||||||
Args: []*configs.Arg{
|
|
||||||
{
|
|
||||||
Index: 0, // the glibc wrapper has the flags at arg2 but the raw syscall has flags at arg0
|
|
||||||
Value: syscall.CLONE_NEWUSER,
|
|
||||||
Op: configs.MaskEqualTo,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
// This is a medium level profile that should be able to do things like installing from
|
|
||||||
// apt-get or yum.
|
|
||||||
var mediumProfile = &securityProfile{
|
|
||||||
Capabilities: []string{
|
|
||||||
"CHOWN",
|
|
||||||
"DAC_OVERRIDE",
|
|
||||||
"FSETID",
|
|
||||||
"FOWNER",
|
|
||||||
"SETGID",
|
|
||||||
"SETUID",
|
|
||||||
"SETFCAP",
|
|
||||||
"SETPCAP",
|
|
||||||
"NET_BIND_SERVICE",
|
|
||||||
"KILL",
|
|
||||||
"AUDIT_WRITE",
|
|
||||||
},
|
|
||||||
Rlimits: []configs.Rlimit{
|
|
||||||
{
|
|
||||||
Type: syscall.RLIMIT_NOFILE,
|
|
||||||
Hard: 1024,
|
|
||||||
Soft: 1024,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
// http://man7.org/linux/man-pages/man2/syscalls.2.html
|
|
||||||
Seccomp: &configs.Seccomp{
|
|
||||||
Syscalls: []*configs.Syscall{
|
|
||||||
{
|
|
||||||
Value: syscall.SYS_UNSHARE, // http://man7.org/linux/man-pages/man2/unshare.2.html
|
|
||||||
Action: configs.Action(syscall.EPERM),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Value: int(system.SysSetns()),
|
|
||||||
Action: configs.Action(syscall.EPERM),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Value: syscall.SYS_MOUNT, // http://man7.org/linux/man-pages/man2/mount.2.html
|
|
||||||
Action: configs.Action(syscall.EPERM),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Value: syscall.SYS_UMOUNT2, // http://man7.org/linux/man-pages/man2/umount.2.html
|
|
||||||
Action: configs.Action(syscall.EPERM),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Value: syscall.SYS_CHROOT, // http://man7.org/linux/man-pages/man2/chroot.2.html
|
|
||||||
Action: configs.Action(syscall.EPERM),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Value: syscall.SYS_CREATE_MODULE, // http://man7.org/linux/man-pages/man2/create_module.2.html
|
|
||||||
Action: configs.Action(syscall.EPERM),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Value: syscall.SYS_DELETE_MODULE, // http://man7.org/linux/man-pages/man2/delete_module.2.html
|
|
||||||
Action: configs.Action(syscall.EPERM),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Value: syscall.SYS_KEXEC_LOAD, // http://man7.org/linux/man-pages/man2/kexec_load.2.html
|
|
||||||
Action: configs.Action(syscall.EPERM),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Value: syscall.SYS_SETDOMAINNAME, // http://man7.org/linux/man-pages/man2/setdomainname.2.html
|
|
||||||
Action: configs.Action(syscall.EPERM),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Value: syscall.SYS_SETHOSTNAME, // http://man7.org/linux/man-pages/man2/sethostname.2.html
|
|
||||||
Action: configs.Action(syscall.EPERM),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Value: syscall.SYS_CLONE, // http://man7.org/linux/man-pages/man2/clone.2.html
|
|
||||||
Action: configs.Action(syscall.EPERM),
|
|
||||||
Args: []*configs.Arg{
|
|
||||||
{
|
|
||||||
Index: 0, // the glibc wrapper has the flags at arg2 but the raw syscall has flags at arg0
|
|
||||||
Value: syscall.CLONE_NEWUSER,
|
|
||||||
Op: configs.MaskEqualTo,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
var lowProfile = &securityProfile{
|
|
||||||
Capabilities: []string{
|
|
||||||
"CHOWN",
|
|
||||||
"DAC_OVERRIDE",
|
|
||||||
"FSETID",
|
|
||||||
"FOWNER",
|
|
||||||
"SETGID",
|
|
||||||
"SETUID",
|
|
||||||
"SYS_CHROOT",
|
|
||||||
"SETFCAP",
|
|
||||||
"SETPCAP",
|
|
||||||
"NET_BIND_SERVICE",
|
|
||||||
"KILL",
|
|
||||||
"AUDIT_WRITE",
|
|
||||||
},
|
|
||||||
Rlimits: []configs.Rlimit{
|
|
||||||
{
|
|
||||||
Type: syscall.RLIMIT_NOFILE,
|
|
||||||
Hard: 1024,
|
|
||||||
Soft: 1024,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
// http://man7.org/linux/man-pages/man2/syscalls.2.html
|
|
||||||
Seccomp: &configs.Seccomp{
|
|
||||||
Syscalls: []*configs.Syscall{
|
|
||||||
{
|
|
||||||
Value: syscall.SYS_UNSHARE, // http://man7.org/linux/man-pages/man2/unshare.2.html
|
|
||||||
Action: configs.Action(syscall.EPERM),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Value: int(system.SysSetns()),
|
|
||||||
Action: configs.Action(syscall.EPERM),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Value: syscall.SYS_MOUNT, // http://man7.org/linux/man-pages/man2/mount.2.html
|
|
||||||
Action: configs.Action(syscall.EPERM),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Value: syscall.SYS_UMOUNT2, // http://man7.org/linux/man-pages/man2/umount.2.html
|
|
||||||
Action: configs.Action(syscall.EPERM),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Value: syscall.SYS_CREATE_MODULE, // http://man7.org/linux/man-pages/man2/create_module.2.html
|
|
||||||
Action: configs.Action(syscall.EPERM),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Value: syscall.SYS_DELETE_MODULE, // http://man7.org/linux/man-pages/man2/delete_module.2.html
|
|
||||||
Action: configs.Action(syscall.EPERM),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Value: syscall.SYS_KEXEC_LOAD, // http://man7.org/linux/man-pages/man2/kexec_load.2.html
|
|
||||||
Action: configs.Action(syscall.EPERM),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Value: syscall.SYS_CLONE, // http://man7.org/linux/man-pages/man2/clone.2.html
|
|
||||||
Action: configs.Action(syscall.EPERM),
|
|
||||||
Args: []*configs.Arg{
|
|
||||||
{
|
|
||||||
Index: 0, // the glibc wrapper has the flags at arg2 but the raw syscall has flags at arg0
|
|
||||||
Value: syscall.CLONE_NEWUSER,
|
|
||||||
Op: configs.MaskEqualTo,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
|
@ -1,31 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/codegangsta/cli"
|
|
||||||
)
|
|
||||||
|
|
||||||
var stateCommand = cli.Command{
|
|
||||||
Name: "state",
|
|
||||||
Usage: "get the container's current state",
|
|
||||||
Flags: []cli.Flag{
|
|
||||||
cli.StringFlag{Name: "id", Value: "nsinit", Usage: "specify the ID for a container"},
|
|
||||||
},
|
|
||||||
Action: func(context *cli.Context) {
|
|
||||||
container, err := getContainer(context)
|
|
||||||
if err != nil {
|
|
||||||
fatal(err)
|
|
||||||
}
|
|
||||||
state, err := container.State()
|
|
||||||
if err != nil {
|
|
||||||
fatal(err)
|
|
||||||
}
|
|
||||||
data, err := json.MarshalIndent(state, "", "\t")
|
|
||||||
if err != nil {
|
|
||||||
fatal(err)
|
|
||||||
}
|
|
||||||
fmt.Printf("%s", data)
|
|
||||||
},
|
|
||||||
}
|
|
|
@ -1,31 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/codegangsta/cli"
|
|
||||||
)
|
|
||||||
|
|
||||||
var statsCommand = cli.Command{
|
|
||||||
Name: "stats",
|
|
||||||
Usage: "display statistics for the container",
|
|
||||||
Flags: []cli.Flag{
|
|
||||||
cli.StringFlag{Name: "id", Value: "nsinit", Usage: "specify the ID for a container"},
|
|
||||||
},
|
|
||||||
Action: func(context *cli.Context) {
|
|
||||||
container, err := getContainer(context)
|
|
||||||
if err != nil {
|
|
||||||
fatal(err)
|
|
||||||
}
|
|
||||||
stats, err := container.Stats()
|
|
||||||
if err != nil {
|
|
||||||
fatal(err)
|
|
||||||
}
|
|
||||||
data, err := json.MarshalIndent(stats, "", "\t")
|
|
||||||
if err != nil {
|
|
||||||
fatal(err)
|
|
||||||
}
|
|
||||||
fmt.Printf("%s", data)
|
|
||||||
},
|
|
||||||
}
|
|
|
@ -1,94 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"io"
|
|
||||||
"os"
|
|
||||||
|
|
||||||
"github.com/codegangsta/cli"
|
|
||||||
"github.com/docker/docker/pkg/term"
|
|
||||||
"github.com/docker/libcontainer"
|
|
||||||
)
|
|
||||||
|
|
||||||
func newTty(context *cli.Context, p *libcontainer.Process, rootuid int) (*tty, error) {
|
|
||||||
if context.Bool("tty") {
|
|
||||||
console, err := p.NewConsole(rootuid)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return &tty{
|
|
||||||
console: console,
|
|
||||||
closers: []io.Closer{
|
|
||||||
console,
|
|
||||||
},
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
return &tty{}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type tty struct {
|
|
||||||
console libcontainer.Console
|
|
||||||
state *term.State
|
|
||||||
closers []io.Closer
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *tty) Close() error {
|
|
||||||
for _, c := range t.closers {
|
|
||||||
c.Close()
|
|
||||||
}
|
|
||||||
if t.state != nil {
|
|
||||||
term.RestoreTerminal(os.Stdin.Fd(), t.state)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *tty) attach(process *libcontainer.Process) error {
|
|
||||||
if t.console != nil {
|
|
||||||
go io.Copy(t.console, os.Stdin)
|
|
||||||
go io.Copy(os.Stdout, t.console)
|
|
||||||
state, err := term.SetRawTerminal(os.Stdin.Fd())
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
t.state = state
|
|
||||||
process.Stderr = nil
|
|
||||||
process.Stdout = nil
|
|
||||||
process.Stdin = nil
|
|
||||||
} else {
|
|
||||||
// setup standard pipes so that the TTY of the calling nsinit process
|
|
||||||
// is not inherited by the container.
|
|
||||||
r, w, err := os.Pipe()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
go io.Copy(w, os.Stdin)
|
|
||||||
t.closers = append(t.closers, w)
|
|
||||||
process.Stdin = r
|
|
||||||
if r, w, err = os.Pipe(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
go io.Copy(os.Stdout, r)
|
|
||||||
process.Stdout = w
|
|
||||||
t.closers = append(t.closers, r)
|
|
||||||
if r, w, err = os.Pipe(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
go io.Copy(os.Stderr, r)
|
|
||||||
process.Stderr = w
|
|
||||||
t.closers = append(t.closers, r)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *tty) setupPipe() {
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *tty) resize() error {
|
|
||||||
if t.console == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
ws, err := term.GetWinsize(os.Stdin.Fd())
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return term.SetWinsize(t.console.Fd(), ws)
|
|
||||||
}
|
|
|
@ -1,79 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
|
|
||||||
"github.com/Sirupsen/logrus"
|
|
||||||
|
|
||||||
"github.com/codegangsta/cli"
|
|
||||||
"github.com/docker/libcontainer"
|
|
||||||
"github.com/docker/libcontainer/cgroups/systemd"
|
|
||||||
"github.com/docker/libcontainer/configs"
|
|
||||||
)
|
|
||||||
|
|
||||||
func loadConfig(context *cli.Context) (*configs.Config, error) {
|
|
||||||
if path := context.String("config"); path != "" {
|
|
||||||
f, err := os.Open(path)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
defer f.Close()
|
|
||||||
var config *configs.Config
|
|
||||||
if err := json.NewDecoder(f).Decode(&config); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return config, nil
|
|
||||||
}
|
|
||||||
config := getTemplate()
|
|
||||||
modify(config, context)
|
|
||||||
return config, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func loadFactory(context *cli.Context) (libcontainer.Factory, error) {
|
|
||||||
cgm := libcontainer.Cgroupfs
|
|
||||||
if context.Bool("systemd") {
|
|
||||||
if systemd.UseSystemd() {
|
|
||||||
cgm = libcontainer.SystemdCgroups
|
|
||||||
} else {
|
|
||||||
logrus.Warn("systemd cgroup flag passed, but systemd support for managing cgroups is not available.")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
root := context.GlobalString("root")
|
|
||||||
abs, err := filepath.Abs(root)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return libcontainer.New(abs, cgm, func(l *libcontainer.LinuxFactory) error {
|
|
||||||
l.CriuPath = context.GlobalString("criu")
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func getContainer(context *cli.Context) (libcontainer.Container, error) {
|
|
||||||
factory, err := loadFactory(context)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
container, err := factory.Load(context.String("id"))
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return container, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
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 fatalf(t string, v ...interface{}) {
|
|
||||||
fmt.Fprintf(os.Stderr, t, v...)
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
Loading…
Reference in New Issue