2015-07-03 00:59:30 +08:00
|
|
|
// +build linux
|
|
|
|
|
2015-06-22 10:31:12 +08:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"encoding/json"
|
|
|
|
"fmt"
|
2015-09-02 00:32:29 +08:00
|
|
|
"io/ioutil"
|
2015-07-03 00:59:30 +08:00
|
|
|
"os"
|
2015-06-22 10:31:12 +08:00
|
|
|
|
2015-07-03 00:59:30 +08:00
|
|
|
"github.com/opencontainers/runc/libcontainer/configs"
|
2016-04-23 21:39:42 +08:00
|
|
|
"github.com/opencontainers/runc/libcontainer/specconv"
|
2016-04-13 04:35:51 +08:00
|
|
|
"github.com/opencontainers/runtime-spec/specs-go"
|
2016-06-07 02:45:46 +08:00
|
|
|
"github.com/urfave/cli"
|
2015-06-22 10:31:12 +08:00
|
|
|
)
|
|
|
|
|
|
|
|
var specCommand = cli.Command{
|
2016-03-19 02:54:06 +08:00
|
|
|
Name: "spec",
|
|
|
|
Usage: "create a new specification file",
|
|
|
|
ArgsUsage: "",
|
|
|
|
Description: `The spec command creates the new specification file named "` + specConfig + `" for
|
|
|
|
the bundle.
|
|
|
|
|
|
|
|
The spec generated is just a starter file. Editing of the spec is required to
|
|
|
|
achieve desired results. For example, the newly generated spec includes an args
|
|
|
|
parameter that is initially set to call the "sh" command when the container is
|
|
|
|
started. Calling "sh" may work for an ubuntu container or busybox, but will not
|
|
|
|
work for containers that do not include the "sh" program.
|
|
|
|
|
|
|
|
EXAMPLE:
|
|
|
|
To run docker's hello-world container one needs to set the args parameter
|
|
|
|
in the spec to call hello. This can be done using the sed command or a text
|
|
|
|
editor. The following commands create a bundle for hello-world, change the
|
|
|
|
default args parameter in the spec from "sh" to "/hello", then run the hello
|
|
|
|
command in a new hello-world container named container1:
|
|
|
|
|
|
|
|
mkdir hello
|
|
|
|
cd hello
|
|
|
|
docker pull hello-world
|
|
|
|
docker export $(docker create hello-world) > hello-world.tar
|
|
|
|
mkdir rootfs
|
|
|
|
tar -C rootfs -xf hello-world.tar
|
|
|
|
runc spec
|
|
|
|
sed -i 's;"sh";"/hello";' ` + specConfig + `
|
2016-06-03 13:53:59 +08:00
|
|
|
runc run container1
|
2016-03-19 02:54:06 +08:00
|
|
|
|
2016-06-03 13:53:59 +08:00
|
|
|
In the run command above, "container1" is the name for the instance of the
|
2016-03-19 02:54:06 +08:00
|
|
|
container that you are starting. The name you provide for the container instance
|
|
|
|
must be unique on your host.
|
|
|
|
|
2016-12-16 14:04:50 +08:00
|
|
|
An alternative for generating a customized spec config is to use "oci-runtime-tool", the
|
|
|
|
sub-command "oci-runtime-tool generate" has lots of options that can be used to do any
|
2017-04-27 21:52:31 +08:00
|
|
|
customizations as you want, see runtime-tools (https://github.com/opencontainers/runtime-tools)
|
2016-05-20 11:46:35 +08:00
|
|
|
to get more information.
|
2016-04-21 12:57:26 +08:00
|
|
|
|
2016-03-19 02:54:06 +08:00
|
|
|
When starting a container through runc, runc needs root privilege. If not
|
|
|
|
already running as root, you can use sudo to give runc root privilege. For
|
|
|
|
example: "sudo runc start container1" will give runc root privilege to start the
|
2017-04-27 21:52:31 +08:00
|
|
|
container on your host.
|
|
|
|
|
|
|
|
Alternatively, you can start a rootless container, which has the ability to run
|
|
|
|
without root privileges. For this to work, the specification file needs to be
|
|
|
|
adjusted accordingly. You can pass the parameter --rootless to this command to
|
Disable rootless mode except RootlessCgMgr when executed as the root in userns
This PR decomposes `libcontainer/configs.Config.Rootless bool` into `RootlessEUID bool` and
`RootlessCgroups bool`, so as to make "runc-in-userns" to be more compatible with "rootful" runc.
`RootlessEUID` denotes that runc is being executed as a non-root user (euid != 0) in
the current user namespace. `RootlessEUID` is almost identical to the former `Rootless`
except cgroups stuff.
`RootlessCgroups` denotes that runc is unlikely to have the full access to cgroups.
`RootlessCgroups` is set to false if runc is executed as the root (euid == 0) in the initial namespace.
Otherwise `RootlessCgroups` is set to true.
(Hint: if `RootlessEUID` is true, `RootlessCgroups` becomes true as well)
When runc is executed as the root (euid == 0) in an user namespace (e.g. by Docker-in-LXD, Podman, Usernetes),
`RootlessEUID` is set to false but `RootlessCgroups` is set to true.
So, "runc-in-userns" behaves almost same as "rootful" runc except that cgroups errors are ignored.
This PR does not have any impact on CLI flags and `state.json`.
Note about CLI:
* Now `runc --rootless=(auto|true|false)` CLI flag is only used for setting `RootlessCgroups`.
* Now `runc spec --rootless` is only required when `RootlessEUID` is set to true.
For runc-in-userns, `runc spec` without `--rootless` should work, when sufficient numbers of
UID/GID are mapped.
Note about `$XDG_RUNTIME_DIR` (e.g. `/run/user/1000`):
* `$XDG_RUNTIME_DIR` is ignored if runc is being executed as the root (euid == 0) in the initial namespace, for backward compatibility.
(`/run/runc` is used)
* If runc is executed as the root (euid == 0) in an user namespace, `$XDG_RUNTIME_DIR` is honored if `$USER != "" && $USER != "root"`.
This allows unprivileged users to allow execute runc as the root in userns, without mounting writable `/run/runc`.
Note about `state.json`:
* `rootless` is set to true when `RootlessEUID == true && RootlessCgroups == true`.
Signed-off-by: Akihiro Suda <suda.akihiro@lab.ntt.co.jp>
2018-07-05 14:28:21 +08:00
|
|
|
generate a proper rootless spec file.
|
|
|
|
|
|
|
|
Note that --rootless is not needed when you execute runc as the root in a user namespace
|
|
|
|
created by an unprivileged user.
|
|
|
|
`,
|
2015-09-02 00:32:29 +08:00
|
|
|
Flags: []cli.Flag{
|
2015-09-16 10:06:59 +08:00
|
|
|
cli.StringFlag{
|
2015-10-28 03:23:44 +08:00
|
|
|
Name: "bundle, b",
|
|
|
|
Value: "",
|
|
|
|
Usage: "path to the root of the bundle directory",
|
2015-09-16 10:06:59 +08:00
|
|
|
},
|
2016-05-09 19:26:11 +08:00
|
|
|
cli.BoolFlag{
|
|
|
|
Name: "rootless",
|
|
|
|
Usage: "generate a configuration for a rootless container",
|
|
|
|
},
|
2015-09-02 00:32:29 +08:00
|
|
|
},
|
2016-05-10 13:58:09 +08:00
|
|
|
Action: func(context *cli.Context) error {
|
2016-10-28 23:43:10 +08:00
|
|
|
if err := checkArgs(context, 0, exactArgs); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2016-05-09 19:26:11 +08:00
|
|
|
spec := specconv.Example()
|
|
|
|
|
|
|
|
rootless := context.Bool("rootless")
|
|
|
|
if rootless {
|
|
|
|
specconv.ToRootless(spec)
|
|
|
|
}
|
2016-02-06 02:46:12 +08:00
|
|
|
|
2015-09-02 00:32:29 +08:00
|
|
|
checkNoFile := func(name string) error {
|
|
|
|
_, err := os.Stat(name)
|
|
|
|
if err == nil {
|
|
|
|
return fmt.Errorf("File %s exists. Remove it first", name)
|
|
|
|
}
|
|
|
|
if !os.IsNotExist(err) {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
2015-10-28 03:23:44 +08:00
|
|
|
bundle := context.String("bundle")
|
|
|
|
if bundle != "" {
|
|
|
|
if err := os.Chdir(bundle); err != nil {
|
2016-05-10 13:58:09 +08:00
|
|
|
return err
|
2015-10-28 03:23:44 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
if err := checkNoFile(specConfig); err != nil {
|
2016-05-10 13:58:09 +08:00
|
|
|
return err
|
2015-09-02 00:32:29 +08:00
|
|
|
}
|
2016-04-23 21:39:42 +08:00
|
|
|
data, err := json.MarshalIndent(spec, "", "\t")
|
2015-06-22 10:31:12 +08:00
|
|
|
if err != nil {
|
2016-05-10 13:58:09 +08:00
|
|
|
return err
|
2015-06-22 10:31:12 +08:00
|
|
|
}
|
2018-10-14 03:14:03 +08:00
|
|
|
return ioutil.WriteFile(specConfig, data, 0666)
|
2015-06-22 10:31:12 +08:00
|
|
|
},
|
|
|
|
}
|
2015-07-03 00:59:30 +08:00
|
|
|
|
|
|
|
// loadSpec loads the specification from the provided path.
|
2016-03-11 06:18:39 +08:00
|
|
|
func loadSpec(cPath string) (spec *specs.Spec, err error) {
|
2015-09-02 00:32:29 +08:00
|
|
|
cf, err := os.Open(cPath)
|
|
|
|
if err != nil {
|
|
|
|
if os.IsNotExist(err) {
|
2016-02-06 02:46:12 +08:00
|
|
|
return nil, fmt.Errorf("JSON specification file %s not found", cPath)
|
2015-09-02 00:32:29 +08:00
|
|
|
}
|
2016-03-06 11:52:26 +08:00
|
|
|
return nil, err
|
2015-07-03 00:59:30 +08:00
|
|
|
}
|
2015-09-16 10:54:53 +08:00
|
|
|
defer cf.Close()
|
|
|
|
|
2015-09-02 00:32:29 +08:00
|
|
|
if err = json.NewDecoder(cf).Decode(&spec); err != nil {
|
2016-03-06 11:52:26 +08:00
|
|
|
return nil, err
|
2015-09-02 00:32:29 +08:00
|
|
|
}
|
2017-06-02 07:17:21 +08:00
|
|
|
return spec, validateProcessSpec(spec.Process)
|
2015-07-03 00:59:30 +08:00
|
|
|
}
|
|
|
|
|
2017-07-13 00:00:49 +08:00
|
|
|
func createLibContainerRlimit(rlimit specs.POSIXRlimit) (configs.Rlimit, error) {
|
2015-09-02 00:32:29 +08:00
|
|
|
rl, err := strToRlimit(rlimit.Type)
|
|
|
|
if err != nil {
|
|
|
|
return configs.Rlimit{}, err
|
|
|
|
}
|
2015-08-23 19:17:31 +08:00
|
|
|
return configs.Rlimit{
|
2015-09-02 00:32:29 +08:00
|
|
|
Type: rl,
|
2017-01-07 08:10:13 +08:00
|
|
|
Hard: rlimit.Hard,
|
|
|
|
Soft: rlimit.Soft,
|
2015-09-02 00:32:29 +08:00
|
|
|
}, nil
|
2015-08-23 19:17:31 +08:00
|
|
|
}
|