| 
									
										
										
										
											2017-02-14 05:12:02 +08:00
										 |  |  | package buildah | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							| 
									
										
										
										
											2018-02-14 03:58:56 +08:00
										 |  |  | 	"bufio" | 
					
						
							| 
									
										
										
										
											2018-03-21 23:19:47 +08:00
										 |  |  | 	"bytes" | 
					
						
							| 
									
										
										
										
											2017-02-14 05:12:02 +08:00
										 |  |  | 	"encoding/json" | 
					
						
							| 
									
										
										
										
											2018-02-14 03:58:56 +08:00
										 |  |  | 	"fmt" | 
					
						
							| 
									
										
										
										
											2018-03-20 19:31:51 +08:00
										 |  |  | 	"io" | 
					
						
							| 
									
										
										
										
											2017-02-14 05:12:02 +08:00
										 |  |  | 	"io/ioutil" | 
					
						
							| 
									
										
										
										
											2018-03-21 23:19:47 +08:00
										 |  |  | 	"net" | 
					
						
							| 
									
										
										
										
											2017-02-14 05:12:02 +08:00
										 |  |  | 	"os" | 
					
						
							|  |  |  | 	"os/exec" | 
					
						
							|  |  |  | 	"path/filepath" | 
					
						
							| 
									
										
											  
											
												Run: use a temporary mount namespace
Move the thread that launches the runtime helper into its own OS thread,
have it create its own mount namespace, and bind mount anything that we
want to eventually bind mount into the container, into a subdirectory of
the bundle directory, before running the helper.
When deciding what to bind mount in, make the volumes specified by the
user our highest priority, in case they've been specified in order to
override our default settings for a given location.  This required
breaking up setupMounts() to keep the complexity tests from complaining.
When we use a user namespace with the host IPC namespace, bind mount
/dev/shm and /dev/mqueue instead of mounting fresh copies.
If we're told to use a user namespace with the host PID namespace,
return an error, because that doesn't work.
When we use a user namespace with the host network namespace, bind mount
/sys instead of mounting a fresh one.
When we use the host UTS namespace, don't try to set a hostname.
Signed-off-by: Nalin Dahyabhai <nalin@redhat.com>
Closes: #700
Approved by: rhatdan
											
										 
											2018-05-03 00:31:05 +08:00
										 |  |  | 	"runtime" | 
					
						
							| 
									
										
										
										
											2018-03-21 23:19:47 +08:00
										 |  |  | 	"strconv" | 
					
						
							| 
									
										
										
										
											2017-02-14 05:12:02 +08:00
										 |  |  | 	"strings" | 
					
						
							| 
									
										
										
										
											2018-03-21 23:19:47 +08:00
										 |  |  | 	"sync" | 
					
						
							|  |  |  | 	"syscall" | 
					
						
							|  |  |  | 	"time" | 
					
						
							| 
									
										
										
										
											2017-02-14 05:12:02 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-14 06:20:25 +08:00
										 |  |  | 	"github.com/containernetworking/cni/libcni" | 
					
						
							| 
									
										
										
										
											2017-02-14 05:12:02 +08:00
										 |  |  | 	"github.com/containers/storage/pkg/ioutils" | 
					
						
							| 
									
										
										
										
											2018-03-21 23:19:47 +08:00
										 |  |  | 	"github.com/containers/storage/pkg/reexec" | 
					
						
							| 
									
										
										
										
											2018-02-14 03:58:56 +08:00
										 |  |  | 	"github.com/docker/docker/profiles/seccomp" | 
					
						
							|  |  |  | 	units "github.com/docker/go-units" | 
					
						
							| 
									
										
										
										
											2017-06-21 05:37:50 +08:00
										 |  |  | 	digest "github.com/opencontainers/go-digest" | 
					
						
							| 
									
										
										
										
											2017-02-18 02:58:34 +08:00
										 |  |  | 	"github.com/opencontainers/runtime-spec/specs-go" | 
					
						
							| 
									
										
										
										
											2017-02-14 05:12:02 +08:00
										 |  |  | 	"github.com/opencontainers/runtime-tools/generate" | 
					
						
							| 
									
										
										
										
											2017-11-29 03:22:47 +08:00
										 |  |  | 	"github.com/opencontainers/selinux/go-selinux/label" | 
					
						
							| 
									
										
										
										
											2017-06-02 03:23:02 +08:00
										 |  |  | 	"github.com/pkg/errors" | 
					
						
							| 
									
										
										
										
											2018-06-07 23:25:47 +08:00
										 |  |  | 	"github.com/projectatomic/buildah/bind" | 
					
						
							| 
									
										
										
										
											2018-04-14 06:20:25 +08:00
										 |  |  | 	"github.com/projectatomic/buildah/util" | 
					
						
							| 
									
										
										
										
											2018-04-27 01:18:34 +08:00
										 |  |  | 	"github.com/projectatomic/libpod/pkg/secrets" | 
					
						
							| 
									
										
										
										
											2017-10-10 03:05:56 +08:00
										 |  |  | 	"github.com/sirupsen/logrus" | 
					
						
							|  |  |  | 	"golang.org/x/crypto/ssh/terminal" | 
					
						
							| 
									
										
										
										
											2018-03-21 23:19:47 +08:00
										 |  |  | 	"golang.org/x/sys/unix" | 
					
						
							| 
									
										
										
										
											2017-02-14 05:12:02 +08:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const ( | 
					
						
							|  |  |  | 	// DefaultWorkingDir is used if none was specified.
 | 
					
						
							|  |  |  | 	DefaultWorkingDir = "/" | 
					
						
							| 
									
										
										
										
											2018-03-21 23:19:47 +08:00
										 |  |  | 	// runUsingRuntimeCommand is a command we use as a key for reexec
 | 
					
						
							|  |  |  | 	runUsingRuntimeCommand = Package + "-runtime" | 
					
						
							| 
									
										
										
										
											2017-02-14 05:12:02 +08:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-29 02:14:57 +08:00
										 |  |  | // TerminalPolicy takes the value DefaultTerminal, WithoutTerminal, or WithTerminal.
 | 
					
						
							|  |  |  | type TerminalPolicy int | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-05 06:36:55 +08:00
										 |  |  | const ( | 
					
						
							| 
									
										
										
										
											2017-04-04 01:43:34 +08:00
										 |  |  | 	// DefaultTerminal indicates that this Run invocation should be
 | 
					
						
							|  |  |  | 	// connected to a pseudoterminal if we're connected to a terminal.
 | 
					
						
							| 
									
										
										
										
											2018-03-29 02:14:57 +08:00
										 |  |  | 	DefaultTerminal TerminalPolicy = iota | 
					
						
							| 
									
										
										
										
											2017-04-04 01:43:34 +08:00
										 |  |  | 	// WithoutTerminal indicates that this Run invocation should NOT be
 | 
					
						
							|  |  |  | 	// connected to a pseudoterminal.
 | 
					
						
							| 
									
										
										
										
											2017-04-05 06:36:55 +08:00
										 |  |  | 	WithoutTerminal | 
					
						
							| 
									
										
										
										
											2017-04-04 01:43:34 +08:00
										 |  |  | 	// WithTerminal indicates that this Run invocation should be connected
 | 
					
						
							|  |  |  | 	// to a pseudoterminal.
 | 
					
						
							| 
									
										
										
										
											2017-04-05 06:36:55 +08:00
										 |  |  | 	WithTerminal | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-29 02:14:57 +08:00
										 |  |  | // String converts a TerminalPoliicy into a string.
 | 
					
						
							|  |  |  | func (t TerminalPolicy) String() string { | 
					
						
							|  |  |  | 	switch t { | 
					
						
							|  |  |  | 	case DefaultTerminal: | 
					
						
							|  |  |  | 		return "DefaultTerminal" | 
					
						
							|  |  |  | 	case WithoutTerminal: | 
					
						
							|  |  |  | 		return "WithoutTerminal" | 
					
						
							|  |  |  | 	case WithTerminal: | 
					
						
							|  |  |  | 		return "WithTerminal" | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return fmt.Sprintf("unrecognized terminal setting %d", t) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-08 07:11:43 +08:00
										 |  |  | // NamespaceOption controls how we set up a namespace when launching processes.
 | 
					
						
							|  |  |  | type NamespaceOption struct { | 
					
						
							|  |  |  | 	// Name specifies the type of namespace, typically matching one of the
 | 
					
						
							|  |  |  | 	// ...Namespace constants defined in
 | 
					
						
							|  |  |  | 	// github.com/opencontainers/runtime-spec/specs-go.
 | 
					
						
							|  |  |  | 	Name string | 
					
						
							|  |  |  | 	// Host is used to force our processes to use the host's namespace of
 | 
					
						
							|  |  |  | 	// this type.
 | 
					
						
							|  |  |  | 	Host bool | 
					
						
							|  |  |  | 	// Path is the path of the namespace to attach our process to, if Host
 | 
					
						
							|  |  |  | 	// is not set.  If Host is not set and Path is also empty, a new
 | 
					
						
							|  |  |  | 	// namespace will be created for the process that we're starting.
 | 
					
						
							| 
									
										
										
										
											2018-04-14 06:20:25 +08:00
										 |  |  | 	// If Name is specs.NetworkNamespace, if Path doesn't look like an
 | 
					
						
							|  |  |  | 	// absolute path, it is treated as a comma-separated list of CNI
 | 
					
						
							|  |  |  | 	// configuration names which will be selected from among all of the CNI
 | 
					
						
							|  |  |  | 	// network configurations which we find.
 | 
					
						
							| 
									
										
										
										
											2018-03-08 07:11:43 +08:00
										 |  |  | 	Path string | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // NamespaceOptions provides some helper methods for a slice of NamespaceOption
 | 
					
						
							|  |  |  | // structs.
 | 
					
						
							|  |  |  | type NamespaceOptions []NamespaceOption | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // IDMappingOptions controls how we set up UID/GID mapping when we set up a
 | 
					
						
							|  |  |  | // user namespace.
 | 
					
						
							|  |  |  | type IDMappingOptions struct { | 
					
						
							|  |  |  | 	HostUIDMapping bool | 
					
						
							|  |  |  | 	HostGIDMapping bool | 
					
						
							|  |  |  | 	UIDMap         []specs.LinuxIDMapping | 
					
						
							|  |  |  | 	GIDMap         []specs.LinuxIDMapping | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-02-14 05:12:02 +08:00
										 |  |  | // RunOptions can be used to alter how a command is run in the container.
 | 
					
						
							|  |  |  | type RunOptions struct { | 
					
						
							|  |  |  | 	// Hostname is the hostname we set for the running container.
 | 
					
						
							|  |  |  | 	Hostname string | 
					
						
							| 
									
										
										
										
											2018-03-08 07:11:43 +08:00
										 |  |  | 	// Runtime is the name of the command to run.  It should accept the same arguments
 | 
					
						
							|  |  |  | 	// that runc does, and produce similar output.
 | 
					
						
							| 
									
										
										
										
											2017-02-18 02:54:49 +08:00
										 |  |  | 	Runtime string | 
					
						
							|  |  |  | 	// Args adds global arguments for the runtime.
 | 
					
						
							|  |  |  | 	Args []string | 
					
						
							| 
									
										
										
										
											2017-02-18 02:58:34 +08:00
										 |  |  | 	// Mounts are additional mount points which we want to provide.
 | 
					
						
							|  |  |  | 	Mounts []specs.Mount | 
					
						
							| 
									
										
										
										
											2017-03-28 02:46:35 +08:00
										 |  |  | 	// Env is additional environment variables to set.
 | 
					
						
							|  |  |  | 	Env []string | 
					
						
							|  |  |  | 	// User is the user as whom to run the command.
 | 
					
						
							|  |  |  | 	User string | 
					
						
							|  |  |  | 	// WorkingDir is an override for the working directory.
 | 
					
						
							|  |  |  | 	WorkingDir string | 
					
						
							| 
									
										
										
										
											2018-03-16 19:57:36 +08:00
										 |  |  | 	// Shell is default shell to run in a container.
 | 
					
						
							|  |  |  | 	Shell string | 
					
						
							| 
									
										
										
										
											2017-03-28 02:46:35 +08:00
										 |  |  | 	// Cmd is an override for the configured default command.
 | 
					
						
							|  |  |  | 	Cmd []string | 
					
						
							|  |  |  | 	// Entrypoint is an override for the configured entry point.
 | 
					
						
							|  |  |  | 	Entrypoint []string | 
					
						
							| 
									
										
										
										
											2018-03-08 07:11:43 +08:00
										 |  |  | 	// NamespaceOptions controls how we set up the namespaces for the process.
 | 
					
						
							|  |  |  | 	NamespaceOptions NamespaceOptions | 
					
						
							| 
									
										
										
										
											2018-04-14 06:20:25 +08:00
										 |  |  | 	// ConfigureNetwork controls whether or not network interfaces and
 | 
					
						
							|  |  |  | 	// routing are configured for a new network namespace (i.e., when not
 | 
					
						
							|  |  |  | 	// joining another's namespace and not just using the host's
 | 
					
						
							|  |  |  | 	// namespace), effectively deciding whether or not the process has a
 | 
					
						
							|  |  |  | 	// usable network.
 | 
					
						
							|  |  |  | 	ConfigureNetwork NetworkConfigurationPolicy | 
					
						
							|  |  |  | 	// CNIPluginPath is the location of CNI plugin helpers, if they should be
 | 
					
						
							|  |  |  | 	// run from a location other than the default location.
 | 
					
						
							|  |  |  | 	CNIPluginPath string | 
					
						
							|  |  |  | 	// CNIConfigDir is the location of CNI configuration files, if the files in
 | 
					
						
							|  |  |  | 	// the default configuration directory shouldn't be used.
 | 
					
						
							|  |  |  | 	CNIConfigDir string | 
					
						
							| 
									
										
										
										
											2017-04-05 06:36:55 +08:00
										 |  |  | 	// Terminal provides a way to specify whether or not the command should
 | 
					
						
							|  |  |  | 	// be run with a pseudoterminal.  By default (DefaultTerminal), a
 | 
					
						
							|  |  |  | 	// terminal is used if os.Stdout is connected to a terminal, but that
 | 
					
						
							|  |  |  | 	// decision can be overridden by specifying either WithTerminal or
 | 
					
						
							|  |  |  | 	// WithoutTerminal.
 | 
					
						
							| 
									
										
										
										
											2018-03-29 02:14:57 +08:00
										 |  |  | 	Terminal TerminalPolicy | 
					
						
							| 
									
										
										
										
											2018-06-12 02:15:00 +08:00
										 |  |  | 	// TerminalSize provides a way to set the number of rows and columns in
 | 
					
						
							|  |  |  | 	// a pseudo-terminal, if we create one, and Stdin/Stdout/Stderr aren't
 | 
					
						
							|  |  |  | 	// connected to a terminal.
 | 
					
						
							|  |  |  | 	TerminalSize *specs.Box | 
					
						
							| 
									
										
										
										
											2018-06-06 01:53:39 +08:00
										 |  |  | 	// The stdin/stdout/stderr descriptors to use.  If set to nil, the
 | 
					
						
							|  |  |  | 	// corresponding files in the "os" package are used as defaults.
 | 
					
						
							| 
									
										
										
										
											2018-06-08 02:41:54 +08:00
										 |  |  | 	Stdin  io.Reader `json:"-"` | 
					
						
							|  |  |  | 	Stdout io.Writer `json:"-"` | 
					
						
							|  |  |  | 	Stderr io.Writer `json:"-"` | 
					
						
							| 
									
										
										
										
											2018-01-24 00:17:31 +08:00
										 |  |  | 	// Quiet tells the run to turn off output to stdout.
 | 
					
						
							|  |  |  | 	Quiet bool | 
					
						
							| 
									
										
										
										
											2018-06-05 05:36:26 +08:00
										 |  |  | 	// AddCapabilities is a list of capabilities to add to the default set.
 | 
					
						
							|  |  |  | 	AddCapabilities []string | 
					
						
							|  |  |  | 	// DropCapabilities is a list of capabilities to remove from the default set,
 | 
					
						
							|  |  |  | 	// after processing the AddCapabilities set.  If a capability appears in both
 | 
					
						
							|  |  |  | 	// lists, it will be dropped.
 | 
					
						
							|  |  |  | 	DropCapabilities []string | 
					
						
							| 
									
										
										
										
											2017-02-14 05:12:02 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-08 07:11:43 +08:00
										 |  |  | // DefaultNamespaceOptions returns the default namespace settings from the
 | 
					
						
							|  |  |  | // runtime-tools generator library.
 | 
					
						
							|  |  |  | func DefaultNamespaceOptions() NamespaceOptions { | 
					
						
							|  |  |  | 	options := NamespaceOptions{ | 
					
						
							|  |  |  | 		{Name: string(specs.CgroupNamespace), Host: true}, | 
					
						
							|  |  |  | 		{Name: string(specs.IPCNamespace), Host: true}, | 
					
						
							|  |  |  | 		{Name: string(specs.MountNamespace), Host: true}, | 
					
						
							|  |  |  | 		{Name: string(specs.NetworkNamespace), Host: true}, | 
					
						
							|  |  |  | 		{Name: string(specs.PIDNamespace), Host: true}, | 
					
						
							|  |  |  | 		{Name: string(specs.UserNamespace), Host: true}, | 
					
						
							|  |  |  | 		{Name: string(specs.UTSNamespace), Host: true}, | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	g := generate.New() | 
					
						
							|  |  |  | 	spec := g.Spec() | 
					
						
							|  |  |  | 	if spec.Linux != nil { | 
					
						
							|  |  |  | 		for _, ns := range spec.Linux.Namespaces { | 
					
						
							|  |  |  | 			options.AddOrReplace(NamespaceOption{ | 
					
						
							|  |  |  | 				Name: string(ns.Type), | 
					
						
							|  |  |  | 				Path: ns.Path, | 
					
						
							|  |  |  | 			}) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return options | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Find the configuration for the namespace of the given type.  If there are
 | 
					
						
							|  |  |  | // duplicates, find the _last_ one of the type, since we assume it was appended
 | 
					
						
							|  |  |  | // more recently.
 | 
					
						
							|  |  |  | func (n *NamespaceOptions) Find(namespace string) *NamespaceOption { | 
					
						
							|  |  |  | 	for i := range *n { | 
					
						
							|  |  |  | 		j := len(*n) - 1 - i | 
					
						
							|  |  |  | 		if (*n)[j].Name == namespace { | 
					
						
							|  |  |  | 			return &((*n)[j]) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // AddOrReplace either adds or replaces the configuration for a given namespace.
 | 
					
						
							|  |  |  | func (n *NamespaceOptions) AddOrReplace(options ...NamespaceOption) { | 
					
						
							|  |  |  | nextOption: | 
					
						
							|  |  |  | 	for _, option := range options { | 
					
						
							|  |  |  | 		for i := range *n { | 
					
						
							|  |  |  | 			j := len(*n) - 1 - i | 
					
						
							|  |  |  | 			if (*n)[j].Name == option.Name { | 
					
						
							|  |  |  | 				(*n)[j] = option | 
					
						
							|  |  |  | 				continue nextOption | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		*n = append(*n, option) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-14 03:58:56 +08:00
										 |  |  | func addRlimits(ulimit []string, g *generate.Generator) error { | 
					
						
							|  |  |  | 	var ( | 
					
						
							|  |  |  | 		ul  *units.Ulimit | 
					
						
							|  |  |  | 		err error | 
					
						
							|  |  |  | 	) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for _, u := range ulimit { | 
					
						
							|  |  |  | 		if ul, err = units.ParseUlimit(u); err != nil { | 
					
						
							|  |  |  | 			return errors.Wrapf(err, "ulimit option %q requires name=SOFT:HARD, failed to be parsed", u) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		g.AddProcessRlimits("RLIMIT_"+strings.ToUpper(ul.Name), uint64(ul.Hard), uint64(ul.Soft)) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-20 19:31:51 +08:00
										 |  |  | func addHosts(hosts []string, w io.Writer) error { | 
					
						
							|  |  |  | 	buf := bufio.NewWriter(w) | 
					
						
							|  |  |  | 	for _, host := range hosts { | 
					
						
							| 
									
										
										
										
											2018-06-16 00:28:03 +08:00
										 |  |  | 		values := strings.SplitN(host, ":", 2) | 
					
						
							|  |  |  | 		if len(values) != 2 { | 
					
						
							|  |  |  | 			return errors.Errorf("unable to parse host entry %q: incorrect format", host) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if values[0] == "" { | 
					
						
							|  |  |  | 			return errors.Errorf("hostname in host entry %q is empty", host) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if values[1] == "" { | 
					
						
							|  |  |  | 			return errors.Errorf("IP address in host entry %q is empty", host) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		fmt.Fprintf(buf, "%s\t%s\n", values[1], values[0]) | 
					
						
							| 
									
										
										
										
											2018-03-20 19:31:51 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	return buf.Flush() | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func addHostsToFile(hosts []string, filename string) error { | 
					
						
							| 
									
										
										
										
											2018-02-22 03:59:25 +08:00
										 |  |  | 	if len(hosts) == 0 { | 
					
						
							|  |  |  | 		return nil | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-03-20 19:31:51 +08:00
										 |  |  | 	file, err := os.OpenFile(filename, os.O_APPEND|os.O_WRONLY, os.ModeAppend) | 
					
						
							| 
									
										
										
										
											2018-02-14 03:58:56 +08:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	defer file.Close() | 
					
						
							| 
									
										
										
										
											2018-03-20 19:31:51 +08:00
										 |  |  | 	return addHosts(hosts, file) | 
					
						
							| 
									
										
										
										
											2018-02-14 03:58:56 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func addCommonOptsToSpec(commonOpts *CommonBuildOptions, g *generate.Generator) error { | 
					
						
							| 
									
										
										
										
											2018-03-07 07:13:24 +08:00
										 |  |  | 	// Resources - CPU
 | 
					
						
							| 
									
										
										
										
											2018-02-14 03:58:56 +08:00
										 |  |  | 	if commonOpts.CPUPeriod != 0 { | 
					
						
							|  |  |  | 		g.SetLinuxResourcesCPUPeriod(commonOpts.CPUPeriod) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if commonOpts.CPUQuota != 0 { | 
					
						
							|  |  |  | 		g.SetLinuxResourcesCPUQuota(commonOpts.CPUQuota) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if commonOpts.CPUShares != 0 { | 
					
						
							|  |  |  | 		g.SetLinuxResourcesCPUShares(commonOpts.CPUShares) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if commonOpts.CPUSetCPUs != "" { | 
					
						
							|  |  |  | 		g.SetLinuxResourcesCPUCpus(commonOpts.CPUSetCPUs) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if commonOpts.CPUSetMems != "" { | 
					
						
							|  |  |  | 		g.SetLinuxResourcesCPUMems(commonOpts.CPUSetMems) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-07 07:13:24 +08:00
										 |  |  | 	// Resources - Memory
 | 
					
						
							| 
									
										
										
										
											2018-02-14 03:58:56 +08:00
										 |  |  | 	if commonOpts.Memory != 0 { | 
					
						
							|  |  |  | 		g.SetLinuxResourcesMemoryLimit(commonOpts.Memory) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if commonOpts.MemorySwap != 0 { | 
					
						
							|  |  |  | 		g.SetLinuxResourcesMemorySwap(commonOpts.MemorySwap) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-07 07:13:24 +08:00
										 |  |  | 	// cgroup membership
 | 
					
						
							| 
									
										
										
										
											2018-02-14 03:58:56 +08:00
										 |  |  | 	if commonOpts.CgroupParent != "" { | 
					
						
							|  |  |  | 		g.SetLinuxCgroupsPath(commonOpts.CgroupParent) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-07 07:13:24 +08:00
										 |  |  | 	// Other process resource limits
 | 
					
						
							| 
									
										
										
										
											2018-02-14 03:58:56 +08:00
										 |  |  | 	if err := addRlimits(commonOpts.Ulimit, g); err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-03-07 07:13:24 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	logrus.Debugf("Resources: %#v", commonOpts) | 
					
						
							| 
									
										
										
										
											2018-02-14 03:58:56 +08:00
										 |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-06-07 23:25:47 +08:00
										 |  |  | func (b *Builder) setupMounts(mountPoint string, spec *specs.Spec, bundlePath string, optionMounts []specs.Mount, bindFiles map[string]string, builtinVolumes, volumeMounts []string, shmSize string, namespaceOptions NamespaceOptions) error { | 
					
						
							| 
									
										
											  
											
												Run: use a temporary mount namespace
Move the thread that launches the runtime helper into its own OS thread,
have it create its own mount namespace, and bind mount anything that we
want to eventually bind mount into the container, into a subdirectory of
the bundle directory, before running the helper.
When deciding what to bind mount in, make the volumes specified by the
user our highest priority, in case they've been specified in order to
override our default settings for a given location.  This required
breaking up setupMounts() to keep the complexity tests from complaining.
When we use a user namespace with the host IPC namespace, bind mount
/dev/shm and /dev/mqueue instead of mounting fresh copies.
If we're told to use a user namespace with the host PID namespace,
return an error, because that doesn't work.
When we use a user namespace with the host network namespace, bind mount
/sys instead of mounting a fresh one.
When we use the host UTS namespace, don't try to set a hostname.
Signed-off-by: Nalin Dahyabhai <nalin@redhat.com>
Closes: #700
Approved by: rhatdan
											
										 
											2018-05-03 00:31:05 +08:00
										 |  |  | 	// Start building a new list of mounts.
 | 
					
						
							| 
									
										
										
										
											2018-05-31 22:56:40 +08:00
										 |  |  | 	var mounts []specs.Mount | 
					
						
							| 
									
										
										
										
											2017-05-24 06:04:13 +08:00
										 |  |  | 	haveMount := func(destination string) bool { | 
					
						
							|  |  |  | 		for _, mount := range mounts { | 
					
						
							|  |  |  | 			if mount.Destination == destination { | 
					
						
							|  |  |  | 				// Already have something to mount there.
 | 
					
						
							|  |  |  | 				return true | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		return false | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
											  
											
												Run: use a temporary mount namespace
Move the thread that launches the runtime helper into its own OS thread,
have it create its own mount namespace, and bind mount anything that we
want to eventually bind mount into the container, into a subdirectory of
the bundle directory, before running the helper.
When deciding what to bind mount in, make the volumes specified by the
user our highest priority, in case they've been specified in order to
override our default settings for a given location.  This required
breaking up setupMounts() to keep the complexity tests from complaining.
When we use a user namespace with the host IPC namespace, bind mount
/dev/shm and /dev/mqueue instead of mounting fresh copies.
If we're told to use a user namespace with the host PID namespace,
return an error, because that doesn't work.
When we use a user namespace with the host network namespace, bind mount
/sys instead of mounting a fresh one.
When we use the host UTS namespace, don't try to set a hostname.
Signed-off-by: Nalin Dahyabhai <nalin@redhat.com>
Closes: #700
Approved by: rhatdan
											
										 
											2018-05-03 00:31:05 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// Copy mounts from the generated list.
 | 
					
						
							|  |  |  | 	mountCgroups := true | 
					
						
							|  |  |  | 	specMounts := []specs.Mount{} | 
					
						
							| 
									
										
										
										
											2017-05-24 06:04:13 +08:00
										 |  |  | 	for _, specMount := range spec.Mounts { | 
					
						
							| 
									
										
											  
											
												Run: use a temporary mount namespace
Move the thread that launches the runtime helper into its own OS thread,
have it create its own mount namespace, and bind mount anything that we
want to eventually bind mount into the container, into a subdirectory of
the bundle directory, before running the helper.
When deciding what to bind mount in, make the volumes specified by the
user our highest priority, in case they've been specified in order to
override our default settings for a given location.  This required
breaking up setupMounts() to keep the complexity tests from complaining.
When we use a user namespace with the host IPC namespace, bind mount
/dev/shm and /dev/mqueue instead of mounting fresh copies.
If we're told to use a user namespace with the host PID namespace,
return an error, because that doesn't work.
When we use a user namespace with the host network namespace, bind mount
/sys instead of mounting a fresh one.
When we use the host UTS namespace, don't try to set a hostname.
Signed-off-by: Nalin Dahyabhai <nalin@redhat.com>
Closes: #700
Approved by: rhatdan
											
										 
											2018-05-03 00:31:05 +08:00
										 |  |  | 		// Override some of the mounts from the generated list if we're doing different things with namespaces.
 | 
					
						
							| 
									
										
										
										
											2018-02-23 01:41:22 +08:00
										 |  |  | 		if specMount.Destination == "/dev/shm" { | 
					
						
							|  |  |  | 			specMount.Options = []string{"nosuid", "noexec", "nodev", "mode=1777", "size=" + shmSize} | 
					
						
							| 
									
										
											  
											
												Run: use a temporary mount namespace
Move the thread that launches the runtime helper into its own OS thread,
have it create its own mount namespace, and bind mount anything that we
want to eventually bind mount into the container, into a subdirectory of
the bundle directory, before running the helper.
When deciding what to bind mount in, make the volumes specified by the
user our highest priority, in case they've been specified in order to
override our default settings for a given location.  This required
breaking up setupMounts() to keep the complexity tests from complaining.
When we use a user namespace with the host IPC namespace, bind mount
/dev/shm and /dev/mqueue instead of mounting fresh copies.
If we're told to use a user namespace with the host PID namespace,
return an error, because that doesn't work.
When we use a user namespace with the host network namespace, bind mount
/sys instead of mounting a fresh one.
When we use the host UTS namespace, don't try to set a hostname.
Signed-off-by: Nalin Dahyabhai <nalin@redhat.com>
Closes: #700
Approved by: rhatdan
											
										 
											2018-05-03 00:31:05 +08:00
										 |  |  | 			user := namespaceOptions.Find(string(specs.UserNamespace)) | 
					
						
							|  |  |  | 			ipc := namespaceOptions.Find(string(specs.IPCNamespace)) | 
					
						
							|  |  |  | 			if (ipc == nil || ipc.Host) && (user != nil && !user.Host) { | 
					
						
							|  |  |  | 				if _, err := os.Stat("/dev/shm"); err != nil && os.IsNotExist(err) { | 
					
						
							|  |  |  | 					continue | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				specMount = specs.Mount{ | 
					
						
							|  |  |  | 					Source:      "/dev/shm", | 
					
						
							|  |  |  | 					Type:        "bind", | 
					
						
							|  |  |  | 					Destination: "/dev/shm", | 
					
						
							| 
									
										
										
										
											2018-06-07 23:25:47 +08:00
										 |  |  | 					Options:     []string{bind.NoBindOption, "rbind", "nosuid", "noexec", "nodev"}, | 
					
						
							| 
									
										
											  
											
												Run: use a temporary mount namespace
Move the thread that launches the runtime helper into its own OS thread,
have it create its own mount namespace, and bind mount anything that we
want to eventually bind mount into the container, into a subdirectory of
the bundle directory, before running the helper.
When deciding what to bind mount in, make the volumes specified by the
user our highest priority, in case they've been specified in order to
override our default settings for a given location.  This required
breaking up setupMounts() to keep the complexity tests from complaining.
When we use a user namespace with the host IPC namespace, bind mount
/dev/shm and /dev/mqueue instead of mounting fresh copies.
If we're told to use a user namespace with the host PID namespace,
return an error, because that doesn't work.
When we use a user namespace with the host network namespace, bind mount
/sys instead of mounting a fresh one.
When we use the host UTS namespace, don't try to set a hostname.
Signed-off-by: Nalin Dahyabhai <nalin@redhat.com>
Closes: #700
Approved by: rhatdan
											
										 
											2018-05-03 00:31:05 +08:00
										 |  |  | 				} | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2018-02-23 01:41:22 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
											  
											
												Run: use a temporary mount namespace
Move the thread that launches the runtime helper into its own OS thread,
have it create its own mount namespace, and bind mount anything that we
want to eventually bind mount into the container, into a subdirectory of
the bundle directory, before running the helper.
When deciding what to bind mount in, make the volumes specified by the
user our highest priority, in case they've been specified in order to
override our default settings for a given location.  This required
breaking up setupMounts() to keep the complexity tests from complaining.
When we use a user namespace with the host IPC namespace, bind mount
/dev/shm and /dev/mqueue instead of mounting fresh copies.
If we're told to use a user namespace with the host PID namespace,
return an error, because that doesn't work.
When we use a user namespace with the host network namespace, bind mount
/sys instead of mounting a fresh one.
When we use the host UTS namespace, don't try to set a hostname.
Signed-off-by: Nalin Dahyabhai <nalin@redhat.com>
Closes: #700
Approved by: rhatdan
											
										 
											2018-05-03 00:31:05 +08:00
										 |  |  | 		if specMount.Destination == "/dev/mqueue" { | 
					
						
							|  |  |  | 			user := namespaceOptions.Find(string(specs.UserNamespace)) | 
					
						
							|  |  |  | 			ipc := namespaceOptions.Find(string(specs.IPCNamespace)) | 
					
						
							|  |  |  | 			if (ipc == nil || ipc.Host) && (user != nil && !user.Host) { | 
					
						
							|  |  |  | 				if _, err := os.Stat("/dev/mqueue"); err != nil && os.IsNotExist(err) { | 
					
						
							|  |  |  | 					continue | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				specMount = specs.Mount{ | 
					
						
							|  |  |  | 					Source:      "/dev/mqueue", | 
					
						
							|  |  |  | 					Type:        "bind", | 
					
						
							|  |  |  | 					Destination: "/dev/mqueue", | 
					
						
							| 
									
										
										
										
											2018-06-07 23:25:47 +08:00
										 |  |  | 					Options:     []string{bind.NoBindOption, "rbind", "nosuid", "noexec", "nodev"}, | 
					
						
							| 
									
										
											  
											
												Run: use a temporary mount namespace
Move the thread that launches the runtime helper into its own OS thread,
have it create its own mount namespace, and bind mount anything that we
want to eventually bind mount into the container, into a subdirectory of
the bundle directory, before running the helper.
When deciding what to bind mount in, make the volumes specified by the
user our highest priority, in case they've been specified in order to
override our default settings for a given location.  This required
breaking up setupMounts() to keep the complexity tests from complaining.
When we use a user namespace with the host IPC namespace, bind mount
/dev/shm and /dev/mqueue instead of mounting fresh copies.
If we're told to use a user namespace with the host PID namespace,
return an error, because that doesn't work.
When we use a user namespace with the host network namespace, bind mount
/sys instead of mounting a fresh one.
When we use the host UTS namespace, don't try to set a hostname.
Signed-off-by: Nalin Dahyabhai <nalin@redhat.com>
Closes: #700
Approved by: rhatdan
											
										 
											2018-05-03 00:31:05 +08:00
										 |  |  | 				} | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2017-05-24 06:04:13 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
											  
											
												Run: use a temporary mount namespace
Move the thread that launches the runtime helper into its own OS thread,
have it create its own mount namespace, and bind mount anything that we
want to eventually bind mount into the container, into a subdirectory of
the bundle directory, before running the helper.
When deciding what to bind mount in, make the volumes specified by the
user our highest priority, in case they've been specified in order to
override our default settings for a given location.  This required
breaking up setupMounts() to keep the complexity tests from complaining.
When we use a user namespace with the host IPC namespace, bind mount
/dev/shm and /dev/mqueue instead of mounting fresh copies.
If we're told to use a user namespace with the host PID namespace,
return an error, because that doesn't work.
When we use a user namespace with the host network namespace, bind mount
/sys instead of mounting a fresh one.
When we use the host UTS namespace, don't try to set a hostname.
Signed-off-by: Nalin Dahyabhai <nalin@redhat.com>
Closes: #700
Approved by: rhatdan
											
										 
											2018-05-03 00:31:05 +08:00
										 |  |  | 		if specMount.Destination == "/sys" { | 
					
						
							|  |  |  | 			user := namespaceOptions.Find(string(specs.UserNamespace)) | 
					
						
							|  |  |  | 			net := namespaceOptions.Find(string(specs.NetworkNamespace)) | 
					
						
							|  |  |  | 			if (net == nil || net.Host) && (user != nil && !user.Host) { | 
					
						
							|  |  |  | 				mountCgroups = false | 
					
						
							|  |  |  | 				if _, err := os.Stat("/sys"); err != nil && os.IsNotExist(err) { | 
					
						
							|  |  |  | 					continue | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				specMount = specs.Mount{ | 
					
						
							|  |  |  | 					Source:      "/sys", | 
					
						
							|  |  |  | 					Type:        "bind", | 
					
						
							|  |  |  | 					Destination: "/sys", | 
					
						
							| 
									
										
										
										
											2018-06-07 23:25:47 +08:00
										 |  |  | 					Options:     []string{bind.NoBindOption, "rbind", "nosuid", "noexec", "nodev", "ro"}, | 
					
						
							| 
									
										
											  
											
												Run: use a temporary mount namespace
Move the thread that launches the runtime helper into its own OS thread,
have it create its own mount namespace, and bind mount anything that we
want to eventually bind mount into the container, into a subdirectory of
the bundle directory, before running the helper.
When deciding what to bind mount in, make the volumes specified by the
user our highest priority, in case they've been specified in order to
override our default settings for a given location.  This required
breaking up setupMounts() to keep the complexity tests from complaining.
When we use a user namespace with the host IPC namespace, bind mount
/dev/shm and /dev/mqueue instead of mounting fresh copies.
If we're told to use a user namespace with the host PID namespace,
return an error, because that doesn't work.
When we use a user namespace with the host network namespace, bind mount
/sys instead of mounting a fresh one.
When we use the host UTS namespace, don't try to set a hostname.
Signed-off-by: Nalin Dahyabhai <nalin@redhat.com>
Closes: #700
Approved by: rhatdan
											
										 
											2018-05-03 00:31:05 +08:00
										 |  |  | 				} | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2017-05-24 06:04:13 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
											  
											
												Run: use a temporary mount namespace
Move the thread that launches the runtime helper into its own OS thread,
have it create its own mount namespace, and bind mount anything that we
want to eventually bind mount into the container, into a subdirectory of
the bundle directory, before running the helper.
When deciding what to bind mount in, make the volumes specified by the
user our highest priority, in case they've been specified in order to
override our default settings for a given location.  This required
breaking up setupMounts() to keep the complexity tests from complaining.
When we use a user namespace with the host IPC namespace, bind mount
/dev/shm and /dev/mqueue instead of mounting fresh copies.
If we're told to use a user namespace with the host PID namespace,
return an error, because that doesn't work.
When we use a user namespace with the host network namespace, bind mount
/sys instead of mounting a fresh one.
When we use the host UTS namespace, don't try to set a hostname.
Signed-off-by: Nalin Dahyabhai <nalin@redhat.com>
Closes: #700
Approved by: rhatdan
											
										 
											2018-05-03 00:31:05 +08:00
										 |  |  | 		specMounts = append(specMounts, specMount) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Add a mount for the cgroups filesystem, unless we're already
 | 
					
						
							|  |  |  | 	// recursively bind mounting all of /sys, in which case we shouldn't
 | 
					
						
							|  |  |  | 	// bother with it.
 | 
					
						
							|  |  |  | 	sysfsMount := []specs.Mount{} | 
					
						
							|  |  |  | 	if mountCgroups { | 
					
						
							|  |  |  | 		sysfsMount = []specs.Mount{{ | 
					
						
							|  |  |  | 			Destination: "/sys/fs/cgroup", | 
					
						
							|  |  |  | 			Type:        "cgroup", | 
					
						
							|  |  |  | 			Source:      "cgroup", | 
					
						
							| 
									
										
										
										
											2018-06-07 23:25:47 +08:00
										 |  |  | 			Options:     []string{bind.NoBindOption, "nosuid", "noexec", "nodev", "relatime", "ro"}, | 
					
						
							| 
									
										
											  
											
												Run: use a temporary mount namespace
Move the thread that launches the runtime helper into its own OS thread,
have it create its own mount namespace, and bind mount anything that we
want to eventually bind mount into the container, into a subdirectory of
the bundle directory, before running the helper.
When deciding what to bind mount in, make the volumes specified by the
user our highest priority, in case they've been specified in order to
override our default settings for a given location.  This required
breaking up setupMounts() to keep the complexity tests from complaining.
When we use a user namespace with the host IPC namespace, bind mount
/dev/shm and /dev/mqueue instead of mounting fresh copies.
If we're told to use a user namespace with the host PID namespace,
return an error, because that doesn't work.
When we use a user namespace with the host network namespace, bind mount
/sys instead of mounting a fresh one.
When we use the host UTS namespace, don't try to set a hostname.
Signed-off-by: Nalin Dahyabhai <nalin@redhat.com>
Closes: #700
Approved by: rhatdan
											
										 
											2018-05-03 00:31:05 +08:00
										 |  |  | 		}} | 
					
						
							| 
									
										
										
										
											2017-05-24 06:04:13 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-11-08 06:44:24 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
											  
											
												Run: use a temporary mount namespace
Move the thread that launches the runtime helper into its own OS thread,
have it create its own mount namespace, and bind mount anything that we
want to eventually bind mount into the container, into a subdirectory of
the bundle directory, before running the helper.
When deciding what to bind mount in, make the volumes specified by the
user our highest priority, in case they've been specified in order to
override our default settings for a given location.  This required
breaking up setupMounts() to keep the complexity tests from complaining.
When we use a user namespace with the host IPC namespace, bind mount
/dev/shm and /dev/mqueue instead of mounting fresh copies.
If we're told to use a user namespace with the host PID namespace,
return an error, because that doesn't work.
When we use a user namespace with the host network namespace, bind mount
/sys instead of mounting a fresh one.
When we use the host UTS namespace, don't try to set a hostname.
Signed-off-by: Nalin Dahyabhai <nalin@redhat.com>
Closes: #700
Approved by: rhatdan
											
										 
											2018-05-03 00:31:05 +08:00
										 |  |  | 	// Get the list of files we need to bind into the container.
 | 
					
						
							| 
									
										
										
										
											2018-06-07 23:25:47 +08:00
										 |  |  | 	bindFileMounts, err := runSetupBoundFiles(bundlePath, bindFiles) | 
					
						
							| 
									
										
											  
											
												Run: use a temporary mount namespace
Move the thread that launches the runtime helper into its own OS thread,
have it create its own mount namespace, and bind mount anything that we
want to eventually bind mount into the container, into a subdirectory of
the bundle directory, before running the helper.
When deciding what to bind mount in, make the volumes specified by the
user our highest priority, in case they've been specified in order to
override our default settings for a given location.  This required
breaking up setupMounts() to keep the complexity tests from complaining.
When we use a user namespace with the host IPC namespace, bind mount
/dev/shm and /dev/mqueue instead of mounting fresh copies.
If we're told to use a user namespace with the host PID namespace,
return an error, because that doesn't work.
When we use a user namespace with the host network namespace, bind mount
/sys instead of mounting a fresh one.
When we use the host UTS namespace, don't try to set a hostname.
Signed-off-by: Nalin Dahyabhai <nalin@redhat.com>
Closes: #700
Approved by: rhatdan
											
										 
											2018-05-03 00:31:05 +08:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// After this point we need to know the per-container persistent storage directory.
 | 
					
						
							| 
									
										
										
										
											2017-11-08 06:44:24 +08:00
										 |  |  | 	cdir, err := b.store.ContainerDirectory(b.ContainerID) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return errors.Wrapf(err, "error determining work directory for container %q", b.ContainerID) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-17 05:19:29 +08:00
										 |  |  | 	// Figure out which UID and GID to tell the secrets package to use
 | 
					
						
							|  |  |  | 	// for files that it creates.
 | 
					
						
							| 
									
										
										
										
											2018-06-07 23:25:47 +08:00
										 |  |  | 	rootUID, rootGID, err := util.GetHostRootIDs(spec) | 
					
						
							| 
									
										
										
										
											2018-03-17 05:19:29 +08:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
											  
											
												Run: use a temporary mount namespace
Move the thread that launches the runtime helper into its own OS thread,
have it create its own mount namespace, and bind mount anything that we
want to eventually bind mount into the container, into a subdirectory of
the bundle directory, before running the helper.
When deciding what to bind mount in, make the volumes specified by the
user our highest priority, in case they've been specified in order to
override our default settings for a given location.  This required
breaking up setupMounts() to keep the complexity tests from complaining.
When we use a user namespace with the host IPC namespace, bind mount
/dev/shm and /dev/mqueue instead of mounting fresh copies.
If we're told to use a user namespace with the host PID namespace,
return an error, because that doesn't work.
When we use a user namespace with the host network namespace, bind mount
/sys instead of mounting a fresh one.
When we use the host UTS namespace, don't try to set a hostname.
Signed-off-by: Nalin Dahyabhai <nalin@redhat.com>
Closes: #700
Approved by: rhatdan
											
										 
											2018-05-03 00:31:05 +08:00
										 |  |  | 	// Get the list of secrets mounts.
 | 
					
						
							| 
									
										
										
										
											2018-03-17 05:19:29 +08:00
										 |  |  | 	secretMounts := secrets.SecretMountsWithUIDGID(b.MountLabel, cdir, b.DefaultMountsFilePath, cdir, int(rootUID), int(rootGID)) | 
					
						
							| 
									
										
											  
											
												Run: use a temporary mount namespace
Move the thread that launches the runtime helper into its own OS thread,
have it create its own mount namespace, and bind mount anything that we
want to eventually bind mount into the container, into a subdirectory of
the bundle directory, before running the helper.
When deciding what to bind mount in, make the volumes specified by the
user our highest priority, in case they've been specified in order to
override our default settings for a given location.  This required
breaking up setupMounts() to keep the complexity tests from complaining.
When we use a user namespace with the host IPC namespace, bind mount
/dev/shm and /dev/mqueue instead of mounting fresh copies.
If we're told to use a user namespace with the host PID namespace,
return an error, because that doesn't work.
When we use a user namespace with the host network namespace, bind mount
/sys instead of mounting a fresh one.
When we use the host UTS namespace, don't try to set a hostname.
Signed-off-by: Nalin Dahyabhai <nalin@redhat.com>
Closes: #700
Approved by: rhatdan
											
										 
											2018-05-03 00:31:05 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// Add temporary copies of the contents of volume locations at the
 | 
					
						
							|  |  |  | 	// volume locations, unless we already have something there.
 | 
					
						
							| 
									
										
										
										
											2018-06-08 22:52:52 +08:00
										 |  |  | 	copyWithTar := b.copyWithTar(nil, nil) | 
					
						
							| 
									
										
											  
											
												Run: use a temporary mount namespace
Move the thread that launches the runtime helper into its own OS thread,
have it create its own mount namespace, and bind mount anything that we
want to eventually bind mount into the container, into a subdirectory of
the bundle directory, before running the helper.
When deciding what to bind mount in, make the volumes specified by the
user our highest priority, in case they've been specified in order to
override our default settings for a given location.  This required
breaking up setupMounts() to keep the complexity tests from complaining.
When we use a user namespace with the host IPC namespace, bind mount
/dev/shm and /dev/mqueue instead of mounting fresh copies.
If we're told to use a user namespace with the host PID namespace,
return an error, because that doesn't work.
When we use a user namespace with the host network namespace, bind mount
/sys instead of mounting a fresh one.
When we use the host UTS namespace, don't try to set a hostname.
Signed-off-by: Nalin Dahyabhai <nalin@redhat.com>
Closes: #700
Approved by: rhatdan
											
										 
											2018-05-03 00:31:05 +08:00
										 |  |  | 	builtins, err := runSetupBuiltinVolumes(b.MountLabel, mountPoint, cdir, copyWithTar, builtinVolumes) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Get the list of explicitly-specified volume mounts.
 | 
					
						
							| 
									
										
										
										
											2018-05-31 22:56:40 +08:00
										 |  |  | 	volumes, err := runSetupVolumeMounts(spec.Linux.MountLabel, volumeMounts, optionMounts) | 
					
						
							| 
									
										
											  
											
												Run: use a temporary mount namespace
Move the thread that launches the runtime helper into its own OS thread,
have it create its own mount namespace, and bind mount anything that we
want to eventually bind mount into the container, into a subdirectory of
the bundle directory, before running the helper.
When deciding what to bind mount in, make the volumes specified by the
user our highest priority, in case they've been specified in order to
override our default settings for a given location.  This required
breaking up setupMounts() to keep the complexity tests from complaining.
When we use a user namespace with the host IPC namespace, bind mount
/dev/shm and /dev/mqueue instead of mounting fresh copies.
If we're told to use a user namespace with the host PID namespace,
return an error, because that doesn't work.
When we use a user namespace with the host network namespace, bind mount
/sys instead of mounting a fresh one.
When we use the host UTS namespace, don't try to set a hostname.
Signed-off-by: Nalin Dahyabhai <nalin@redhat.com>
Closes: #700
Approved by: rhatdan
											
										 
											2018-05-03 00:31:05 +08:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Add them all, in the preferred order, except where they conflict with something that was previously added.
 | 
					
						
							|  |  |  | 	for _, mount := range append(append(append(append(append(volumes, builtins...), secretMounts...), bindFileMounts...), specMounts...), sysfsMount...) { | 
					
						
							| 
									
										
										
										
											2018-04-27 01:18:34 +08:00
										 |  |  | 		if haveMount(mount.Destination) { | 
					
						
							| 
									
										
											  
											
												Run: use a temporary mount namespace
Move the thread that launches the runtime helper into its own OS thread,
have it create its own mount namespace, and bind mount anything that we
want to eventually bind mount into the container, into a subdirectory of
the bundle directory, before running the helper.
When deciding what to bind mount in, make the volumes specified by the
user our highest priority, in case they've been specified in order to
override our default settings for a given location.  This required
breaking up setupMounts() to keep the complexity tests from complaining.
When we use a user namespace with the host IPC namespace, bind mount
/dev/shm and /dev/mqueue instead of mounting fresh copies.
If we're told to use a user namespace with the host PID namespace,
return an error, because that doesn't work.
When we use a user namespace with the host network namespace, bind mount
/sys instead of mounting a fresh one.
When we use the host UTS namespace, don't try to set a hostname.
Signed-off-by: Nalin Dahyabhai <nalin@redhat.com>
Closes: #700
Approved by: rhatdan
											
										 
											2018-05-03 00:31:05 +08:00
										 |  |  | 			// Already mounting something there, no need to bother with this one.
 | 
					
						
							| 
									
										
										
										
											2018-01-04 04:40:55 +08:00
										 |  |  | 			continue | 
					
						
							| 
									
										
										
										
											2017-11-08 06:44:24 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
											  
											
												Run: use a temporary mount namespace
Move the thread that launches the runtime helper into its own OS thread,
have it create its own mount namespace, and bind mount anything that we
want to eventually bind mount into the container, into a subdirectory of
the bundle directory, before running the helper.
When deciding what to bind mount in, make the volumes specified by the
user our highest priority, in case they've been specified in order to
override our default settings for a given location.  This required
breaking up setupMounts() to keep the complexity tests from complaining.
When we use a user namespace with the host IPC namespace, bind mount
/dev/shm and /dev/mqueue instead of mounting fresh copies.
If we're told to use a user namespace with the host PID namespace,
return an error, because that doesn't work.
When we use a user namespace with the host network namespace, bind mount
/sys instead of mounting a fresh one.
When we use the host UTS namespace, don't try to set a hostname.
Signed-off-by: Nalin Dahyabhai <nalin@redhat.com>
Closes: #700
Approved by: rhatdan
											
										 
											2018-05-03 00:31:05 +08:00
										 |  |  | 		// Add the mount.
 | 
					
						
							| 
									
										
										
										
											2018-04-27 01:18:34 +08:00
										 |  |  | 		mounts = append(mounts, mount) | 
					
						
							| 
									
										
										
										
											2017-11-08 06:44:24 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-04-27 01:18:34 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
											  
											
												Run: use a temporary mount namespace
Move the thread that launches the runtime helper into its own OS thread,
have it create its own mount namespace, and bind mount anything that we
want to eventually bind mount into the container, into a subdirectory of
the bundle directory, before running the helper.
When deciding what to bind mount in, make the volumes specified by the
user our highest priority, in case they've been specified in order to
override our default settings for a given location.  This required
breaking up setupMounts() to keep the complexity tests from complaining.
When we use a user namespace with the host IPC namespace, bind mount
/dev/shm and /dev/mqueue instead of mounting fresh copies.
If we're told to use a user namespace with the host PID namespace,
return an error, because that doesn't work.
When we use a user namespace with the host network namespace, bind mount
/sys instead of mounting a fresh one.
When we use the host UTS namespace, don't try to set a hostname.
Signed-off-by: Nalin Dahyabhai <nalin@redhat.com>
Closes: #700
Approved by: rhatdan
											
										 
											2018-05-03 00:31:05 +08:00
										 |  |  | 	// Set the list in the spec.
 | 
					
						
							|  |  |  | 	spec.Mounts = mounts | 
					
						
							|  |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-06-07 23:25:47 +08:00
										 |  |  | func runSetupBoundFiles(bundlePath string, bindFiles map[string]string) (mounts []specs.Mount, err error) { | 
					
						
							| 
									
										
											  
											
												Run: use a temporary mount namespace
Move the thread that launches the runtime helper into its own OS thread,
have it create its own mount namespace, and bind mount anything that we
want to eventually bind mount into the container, into a subdirectory of
the bundle directory, before running the helper.
When deciding what to bind mount in, make the volumes specified by the
user our highest priority, in case they've been specified in order to
override our default settings for a given location.  This required
breaking up setupMounts() to keep the complexity tests from complaining.
When we use a user namespace with the host IPC namespace, bind mount
/dev/shm and /dev/mqueue instead of mounting fresh copies.
If we're told to use a user namespace with the host PID namespace,
return an error, because that doesn't work.
When we use a user namespace with the host network namespace, bind mount
/sys instead of mounting a fresh one.
When we use the host UTS namespace, don't try to set a hostname.
Signed-off-by: Nalin Dahyabhai <nalin@redhat.com>
Closes: #700
Approved by: rhatdan
											
										 
											2018-05-03 00:31:05 +08:00
										 |  |  | 	for dest, src := range bindFiles { | 
					
						
							| 
									
										
										
										
											2018-06-07 23:25:47 +08:00
										 |  |  | 		options := []string{"rbind"} | 
					
						
							|  |  |  | 		if strings.HasPrefix(src, bundlePath) { | 
					
						
							|  |  |  | 			options = append(options, bind.NoBindOption) | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
											  
											
												Run: use a temporary mount namespace
Move the thread that launches the runtime helper into its own OS thread,
have it create its own mount namespace, and bind mount anything that we
want to eventually bind mount into the container, into a subdirectory of
the bundle directory, before running the helper.
When deciding what to bind mount in, make the volumes specified by the
user our highest priority, in case they've been specified in order to
override our default settings for a given location.  This required
breaking up setupMounts() to keep the complexity tests from complaining.
When we use a user namespace with the host IPC namespace, bind mount
/dev/shm and /dev/mqueue instead of mounting fresh copies.
If we're told to use a user namespace with the host PID namespace,
return an error, because that doesn't work.
When we use a user namespace with the host network namespace, bind mount
/sys instead of mounting a fresh one.
When we use the host UTS namespace, don't try to set a hostname.
Signed-off-by: Nalin Dahyabhai <nalin@redhat.com>
Closes: #700
Approved by: rhatdan
											
										 
											2018-05-03 00:31:05 +08:00
										 |  |  | 		mounts = append(mounts, specs.Mount{ | 
					
						
							|  |  |  | 			Source:      src, | 
					
						
							|  |  |  | 			Destination: dest, | 
					
						
							|  |  |  | 			Type:        "bind", | 
					
						
							| 
									
										
										
										
											2018-06-07 23:25:47 +08:00
										 |  |  | 			Options:     options, | 
					
						
							| 
									
										
											  
											
												Run: use a temporary mount namespace
Move the thread that launches the runtime helper into its own OS thread,
have it create its own mount namespace, and bind mount anything that we
want to eventually bind mount into the container, into a subdirectory of
the bundle directory, before running the helper.
When deciding what to bind mount in, make the volumes specified by the
user our highest priority, in case they've been specified in order to
override our default settings for a given location.  This required
breaking up setupMounts() to keep the complexity tests from complaining.
When we use a user namespace with the host IPC namespace, bind mount
/dev/shm and /dev/mqueue instead of mounting fresh copies.
If we're told to use a user namespace with the host PID namespace,
return an error, because that doesn't work.
When we use a user namespace with the host network namespace, bind mount
/sys instead of mounting a fresh one.
When we use the host UTS namespace, don't try to set a hostname.
Signed-off-by: Nalin Dahyabhai <nalin@redhat.com>
Closes: #700
Approved by: rhatdan
											
										 
											2018-05-03 00:31:05 +08:00
										 |  |  | 		}) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return mounts, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func runSetupBuiltinVolumes(mountLabel, mountPoint, containerDir string, copyWithTar func(srcPath, dstPath string) error, builtinVolumes []string) ([]specs.Mount, error) { | 
					
						
							|  |  |  | 	var mounts []specs.Mount | 
					
						
							| 
									
										
										
										
											2017-06-21 05:37:50 +08:00
										 |  |  | 	// Add temporary copies of the contents of volume locations at the
 | 
					
						
							|  |  |  | 	// volume locations, unless we already have something there.
 | 
					
						
							| 
									
										
										
										
											2018-02-23 01:41:22 +08:00
										 |  |  | 	for _, volume := range builtinVolumes { | 
					
						
							| 
									
										
										
										
											2017-06-21 05:37:50 +08:00
										 |  |  | 		subdir := digest.Canonical.FromString(volume).Hex() | 
					
						
							| 
									
										
											  
											
												Run: use a temporary mount namespace
Move the thread that launches the runtime helper into its own OS thread,
have it create its own mount namespace, and bind mount anything that we
want to eventually bind mount into the container, into a subdirectory of
the bundle directory, before running the helper.
When deciding what to bind mount in, make the volumes specified by the
user our highest priority, in case they've been specified in order to
override our default settings for a given location.  This required
breaking up setupMounts() to keep the complexity tests from complaining.
When we use a user namespace with the host IPC namespace, bind mount
/dev/shm and /dev/mqueue instead of mounting fresh copies.
If we're told to use a user namespace with the host PID namespace,
return an error, because that doesn't work.
When we use a user namespace with the host network namespace, bind mount
/sys instead of mounting a fresh one.
When we use the host UTS namespace, don't try to set a hostname.
Signed-off-by: Nalin Dahyabhai <nalin@redhat.com>
Closes: #700
Approved by: rhatdan
											
										 
											2018-05-03 00:31:05 +08:00
										 |  |  | 		volumePath := filepath.Join(containerDir, "buildah-volumes", subdir) | 
					
						
							| 
									
										
										
										
											2017-06-21 05:37:50 +08:00
										 |  |  | 		// If we need to, initialize the volume path's initial contents.
 | 
					
						
							| 
									
										
											  
											
												Run: use a temporary mount namespace
Move the thread that launches the runtime helper into its own OS thread,
have it create its own mount namespace, and bind mount anything that we
want to eventually bind mount into the container, into a subdirectory of
the bundle directory, before running the helper.
When deciding what to bind mount in, make the volumes specified by the
user our highest priority, in case they've been specified in order to
override our default settings for a given location.  This required
breaking up setupMounts() to keep the complexity tests from complaining.
When we use a user namespace with the host IPC namespace, bind mount
/dev/shm and /dev/mqueue instead of mounting fresh copies.
If we're told to use a user namespace with the host PID namespace,
return an error, because that doesn't work.
When we use a user namespace with the host network namespace, bind mount
/sys instead of mounting a fresh one.
When we use the host UTS namespace, don't try to set a hostname.
Signed-off-by: Nalin Dahyabhai <nalin@redhat.com>
Closes: #700
Approved by: rhatdan
											
										 
											2018-05-03 00:31:05 +08:00
										 |  |  | 		if _, err := os.Stat(volumePath); os.IsNotExist(err) { | 
					
						
							| 
									
										
										
										
											2017-06-21 05:37:50 +08:00
										 |  |  | 			if err = os.MkdirAll(volumePath, 0755); err != nil { | 
					
						
							| 
									
										
											  
											
												Run: use a temporary mount namespace
Move the thread that launches the runtime helper into its own OS thread,
have it create its own mount namespace, and bind mount anything that we
want to eventually bind mount into the container, into a subdirectory of
the bundle directory, before running the helper.
When deciding what to bind mount in, make the volumes specified by the
user our highest priority, in case they've been specified in order to
override our default settings for a given location.  This required
breaking up setupMounts() to keep the complexity tests from complaining.
When we use a user namespace with the host IPC namespace, bind mount
/dev/shm and /dev/mqueue instead of mounting fresh copies.
If we're told to use a user namespace with the host PID namespace,
return an error, because that doesn't work.
When we use a user namespace with the host network namespace, bind mount
/sys instead of mounting a fresh one.
When we use the host UTS namespace, don't try to set a hostname.
Signed-off-by: Nalin Dahyabhai <nalin@redhat.com>
Closes: #700
Approved by: rhatdan
											
										 
											2018-05-03 00:31:05 +08:00
										 |  |  | 				return nil, errors.Wrapf(err, "error creating directory %q for volume %q", volumePath, volume) | 
					
						
							| 
									
										
										
										
											2017-06-21 05:37:50 +08:00
										 |  |  | 			} | 
					
						
							| 
									
										
											  
											
												Run: use a temporary mount namespace
Move the thread that launches the runtime helper into its own OS thread,
have it create its own mount namespace, and bind mount anything that we
want to eventually bind mount into the container, into a subdirectory of
the bundle directory, before running the helper.
When deciding what to bind mount in, make the volumes specified by the
user our highest priority, in case they've been specified in order to
override our default settings for a given location.  This required
breaking up setupMounts() to keep the complexity tests from complaining.
When we use a user namespace with the host IPC namespace, bind mount
/dev/shm and /dev/mqueue instead of mounting fresh copies.
If we're told to use a user namespace with the host PID namespace,
return an error, because that doesn't work.
When we use a user namespace with the host network namespace, bind mount
/sys instead of mounting a fresh one.
When we use the host UTS namespace, don't try to set a hostname.
Signed-off-by: Nalin Dahyabhai <nalin@redhat.com>
Closes: #700
Approved by: rhatdan
											
										 
											2018-05-03 00:31:05 +08:00
										 |  |  | 			if err = label.Relabel(volumePath, mountLabel, false); err != nil { | 
					
						
							|  |  |  | 				return nil, errors.Wrapf(err, "error relabeling directory %q for volume %q", volumePath, volume) | 
					
						
							| 
									
										
										
										
											2017-11-29 03:22:47 +08:00
										 |  |  | 			} | 
					
						
							| 
									
										
										
										
											2017-06-21 05:37:50 +08:00
										 |  |  | 			srcPath := filepath.Join(mountPoint, volume) | 
					
						
							| 
									
										
										
										
											2018-02-12 23:17:25 +08:00
										 |  |  | 			if err = copyWithTar(srcPath, volumePath); err != nil && !os.IsNotExist(err) { | 
					
						
							| 
									
										
											  
											
												Run: use a temporary mount namespace
Move the thread that launches the runtime helper into its own OS thread,
have it create its own mount namespace, and bind mount anything that we
want to eventually bind mount into the container, into a subdirectory of
the bundle directory, before running the helper.
When deciding what to bind mount in, make the volumes specified by the
user our highest priority, in case they've been specified in order to
override our default settings for a given location.  This required
breaking up setupMounts() to keep the complexity tests from complaining.
When we use a user namespace with the host IPC namespace, bind mount
/dev/shm and /dev/mqueue instead of mounting fresh copies.
If we're told to use a user namespace with the host PID namespace,
return an error, because that doesn't work.
When we use a user namespace with the host network namespace, bind mount
/sys instead of mounting a fresh one.
When we use the host UTS namespace, don't try to set a hostname.
Signed-off-by: Nalin Dahyabhai <nalin@redhat.com>
Closes: #700
Approved by: rhatdan
											
										 
											2018-05-03 00:31:05 +08:00
										 |  |  | 				return nil, errors.Wrapf(err, "error populating directory %q for volume %q using contents of %q", volumePath, volume, srcPath) | 
					
						
							| 
									
										
										
										
											2017-06-21 05:37:50 +08:00
										 |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		// Add the bind mount.
 | 
					
						
							| 
									
										
										
										
											2017-05-24 06:04:13 +08:00
										 |  |  | 		mounts = append(mounts, specs.Mount{ | 
					
						
							| 
									
										
										
										
											2017-06-21 05:37:50 +08:00
										 |  |  | 			Source:      volumePath, | 
					
						
							| 
									
										
										
										
											2017-05-24 06:04:13 +08:00
										 |  |  | 			Destination: volume, | 
					
						
							| 
									
										
										
										
											2017-06-21 05:37:50 +08:00
										 |  |  | 			Type:        "bind", | 
					
						
							|  |  |  | 			Options:     []string{"bind"}, | 
					
						
							| 
									
										
										
										
											2017-05-24 06:04:13 +08:00
										 |  |  | 		}) | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
											  
											
												Run: use a temporary mount namespace
Move the thread that launches the runtime helper into its own OS thread,
have it create its own mount namespace, and bind mount anything that we
want to eventually bind mount into the container, into a subdirectory of
the bundle directory, before running the helper.
When deciding what to bind mount in, make the volumes specified by the
user our highest priority, in case they've been specified in order to
override our default settings for a given location.  This required
breaking up setupMounts() to keep the complexity tests from complaining.
When we use a user namespace with the host IPC namespace, bind mount
/dev/shm and /dev/mqueue instead of mounting fresh copies.
If we're told to use a user namespace with the host PID namespace,
return an error, because that doesn't work.
When we use a user namespace with the host network namespace, bind mount
/sys instead of mounting a fresh one.
When we use the host UTS namespace, don't try to set a hostname.
Signed-off-by: Nalin Dahyabhai <nalin@redhat.com>
Closes: #700
Approved by: rhatdan
											
										 
											2018-05-03 00:31:05 +08:00
										 |  |  | 	return mounts, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-05-31 22:56:40 +08:00
										 |  |  | func runSetupVolumeMounts(mountLabel string, volumeMounts []string, optionMounts []specs.Mount) ([]specs.Mount, error) { | 
					
						
							| 
									
										
											  
											
												Run: use a temporary mount namespace
Move the thread that launches the runtime helper into its own OS thread,
have it create its own mount namespace, and bind mount anything that we
want to eventually bind mount into the container, into a subdirectory of
the bundle directory, before running the helper.
When deciding what to bind mount in, make the volumes specified by the
user our highest priority, in case they've been specified in order to
override our default settings for a given location.  This required
breaking up setupMounts() to keep the complexity tests from complaining.
When we use a user namespace with the host IPC namespace, bind mount
/dev/shm and /dev/mqueue instead of mounting fresh copies.
If we're told to use a user namespace with the host PID namespace,
return an error, because that doesn't work.
When we use a user namespace with the host network namespace, bind mount
/sys instead of mounting a fresh one.
When we use the host UTS namespace, don't try to set a hostname.
Signed-off-by: Nalin Dahyabhai <nalin@redhat.com>
Closes: #700
Approved by: rhatdan
											
										 
											2018-05-03 00:31:05 +08:00
										 |  |  | 	var mounts []specs.Mount | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-05-31 22:56:40 +08:00
										 |  |  | 	parseMount := func(host, container string, options []string) (specs.Mount, error) { | 
					
						
							| 
									
										
										
										
											2018-02-23 01:41:22 +08:00
										 |  |  | 		var foundrw, foundro, foundz, foundZ bool | 
					
						
							|  |  |  | 		var rootProp string | 
					
						
							|  |  |  | 		for _, opt := range options { | 
					
						
							|  |  |  | 			switch opt { | 
					
						
							|  |  |  | 			case "rw": | 
					
						
							|  |  |  | 				foundrw = true | 
					
						
							|  |  |  | 			case "ro": | 
					
						
							|  |  |  | 				foundro = true | 
					
						
							|  |  |  | 			case "z": | 
					
						
							|  |  |  | 				foundz = true | 
					
						
							|  |  |  | 			case "Z": | 
					
						
							|  |  |  | 				foundZ = true | 
					
						
							|  |  |  | 			case "private", "rprivate", "slave", "rslave", "shared", "rshared": | 
					
						
							|  |  |  | 				rootProp = opt | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if !foundrw && !foundro { | 
					
						
							|  |  |  | 			options = append(options, "rw") | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if foundz { | 
					
						
							| 
									
										
										
										
											2018-05-31 22:56:40 +08:00
										 |  |  | 			if err := label.Relabel(host, mountLabel, true); err != nil { | 
					
						
							|  |  |  | 				return specs.Mount{}, errors.Wrapf(err, "relabeling %q failed", host) | 
					
						
							| 
									
										
										
										
											2018-02-23 01:41:22 +08:00
										 |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if foundZ { | 
					
						
							| 
									
										
										
										
											2018-05-31 22:56:40 +08:00
										 |  |  | 			if err := label.Relabel(host, mountLabel, false); err != nil { | 
					
						
							|  |  |  | 				return specs.Mount{}, errors.Wrapf(err, "relabeling %q failed", host) | 
					
						
							| 
									
										
										
										
											2018-02-23 01:41:22 +08:00
										 |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if rootProp == "" { | 
					
						
							|  |  |  | 			options = append(options, "private") | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2018-05-31 22:56:40 +08:00
										 |  |  | 		return specs.Mount{ | 
					
						
							|  |  |  | 			Destination: container, | 
					
						
							| 
									
										
										
										
											2018-02-23 01:41:22 +08:00
										 |  |  | 			Type:        "bind", | 
					
						
							| 
									
										
										
										
											2018-05-31 22:56:40 +08:00
										 |  |  | 			Source:      host, | 
					
						
							| 
									
										
										
										
											2018-02-23 01:41:22 +08:00
										 |  |  | 			Options:     options, | 
					
						
							| 
									
										
										
										
											2018-05-31 22:56:40 +08:00
										 |  |  | 		}, nil | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	// Bind mount volumes specified for this particular Run() invocation
 | 
					
						
							|  |  |  | 	for _, i := range optionMounts { | 
					
						
							|  |  |  | 		mount, err := parseMount(i.Source, i.Destination, append(i.Options, "rbind")) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return nil, err | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		mounts = append(mounts, mount) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	// Bind mount volumes given by the user when the container was created
 | 
					
						
							|  |  |  | 	for _, i := range volumeMounts { | 
					
						
							|  |  |  | 		var options []string | 
					
						
							|  |  |  | 		spliti := strings.Split(i, ":") | 
					
						
							|  |  |  | 		if len(spliti) > 2 { | 
					
						
							|  |  |  | 			options = strings.Split(spliti[2], ",") | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		options = append(options, "rbind") | 
					
						
							|  |  |  | 		mount, err := parseMount(spliti[0], spliti[1], options) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return nil, err | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		mounts = append(mounts, mount) | 
					
						
							| 
									
										
										
										
											2018-02-23 01:41:22 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
											  
											
												Run: use a temporary mount namespace
Move the thread that launches the runtime helper into its own OS thread,
have it create its own mount namespace, and bind mount anything that we
want to eventually bind mount into the container, into a subdirectory of
the bundle directory, before running the helper.
When deciding what to bind mount in, make the volumes specified by the
user our highest priority, in case they've been specified in order to
override our default settings for a given location.  This required
breaking up setupMounts() to keep the complexity tests from complaining.
When we use a user namespace with the host IPC namespace, bind mount
/dev/shm and /dev/mqueue instead of mounting fresh copies.
If we're told to use a user namespace with the host PID namespace,
return an error, because that doesn't work.
When we use a user namespace with the host network namespace, bind mount
/sys instead of mounting a fresh one.
When we use the host UTS namespace, don't try to set a hostname.
Signed-off-by: Nalin Dahyabhai <nalin@redhat.com>
Closes: #700
Approved by: rhatdan
											
										 
											2018-05-03 00:31:05 +08:00
										 |  |  | 	return mounts, nil | 
					
						
							| 
									
										
										
										
											2017-05-24 06:04:13 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-05-19 04:00:58 +08:00
										 |  |  | // addNetworkConfig copies files from host and sets them up to bind mount into container
 | 
					
						
							|  |  |  | func (b *Builder) addNetworkConfig(rdir, hostPath string) (string, error) { | 
					
						
							| 
									
										
										
										
											2018-06-08 22:52:52 +08:00
										 |  |  | 	copyFileWithTar := b.copyFileWithTar(nil, nil) | 
					
						
							| 
									
										
										
										
											2018-05-19 04:00:58 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	cfile := filepath.Join(rdir, filepath.Base(hostPath)) | 
					
						
							| 
									
										
										
										
											2018-03-13 01:53:12 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	if err := copyFileWithTar(hostPath, cfile); err != nil { | 
					
						
							|  |  |  | 		return "", errors.Wrapf(err, "error copying %q for container %q", cfile, b.ContainerID) | 
					
						
							| 
									
										
										
										
											2018-05-19 04:00:58 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-03-13 01:53:12 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	if err := label.Relabel(cfile, b.MountLabel, false); err != nil { | 
					
						
							| 
									
										
										
										
											2018-05-19 04:00:58 +08:00
										 |  |  | 		return "", errors.Wrapf(err, "error relabeling %q in container %q", cfile, b.ContainerID) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return cfile, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-08 07:11:43 +08:00
										 |  |  | func setupMaskedPaths(g *generate.Generator) { | 
					
						
							|  |  |  | 	for _, mp := range []string{ | 
					
						
							|  |  |  | 		"/proc/kcore", | 
					
						
							|  |  |  | 		"/proc/latency_stats", | 
					
						
							|  |  |  | 		"/proc/timer_list", | 
					
						
							|  |  |  | 		"/proc/timer_stats", | 
					
						
							|  |  |  | 		"/proc/sched_debug", | 
					
						
							|  |  |  | 		"/proc/scsi", | 
					
						
							|  |  |  | 		"/sys/firmware", | 
					
						
							|  |  |  | 	} { | 
					
						
							|  |  |  | 		g.AddLinuxMaskedPaths(mp) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func setupReadOnlyPaths(g *generate.Generator) { | 
					
						
							|  |  |  | 	for _, rp := range []string{ | 
					
						
							|  |  |  | 		"/proc/asound", | 
					
						
							|  |  |  | 		"/proc/bus", | 
					
						
							|  |  |  | 		"/proc/fs", | 
					
						
							|  |  |  | 		"/proc/irq", | 
					
						
							|  |  |  | 		"/proc/sys", | 
					
						
							|  |  |  | 		"/proc/sysrq-trigger", | 
					
						
							|  |  |  | 	} { | 
					
						
							|  |  |  | 		g.AddLinuxReadonlyPaths(rp) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-06-05 05:36:26 +08:00
										 |  |  | func setupCapAdd(g *generate.Generator, caps ...string) error { | 
					
						
							|  |  |  | 	for _, cap := range caps { | 
					
						
							|  |  |  | 		if err := g.AddProcessCapabilityBounding(cap); err != nil { | 
					
						
							|  |  |  | 			return errors.Wrapf(err, "error adding %q to the bounding capability set", cap) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if err := g.AddProcessCapabilityEffective(cap); err != nil { | 
					
						
							|  |  |  | 			return errors.Wrapf(err, "error adding %q to the effective capability set", cap) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if err := g.AddProcessCapabilityInheritable(cap); err != nil { | 
					
						
							|  |  |  | 			return errors.Wrapf(err, "error adding %q to the inheritable capability set", cap) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if err := g.AddProcessCapabilityPermitted(cap); err != nil { | 
					
						
							|  |  |  | 			return errors.Wrapf(err, "error adding %q to the permitted capability set", cap) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if err := g.AddProcessCapabilityAmbient(cap); err != nil { | 
					
						
							|  |  |  | 			return errors.Wrapf(err, "error adding %q to the ambient capability set", cap) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func setupCapDrop(g *generate.Generator, caps ...string) error { | 
					
						
							|  |  |  | 	for _, cap := range caps { | 
					
						
							|  |  |  | 		if err := g.DropProcessCapabilityBounding(cap); err != nil { | 
					
						
							|  |  |  | 			return errors.Wrapf(err, "error removing %q from the bounding capability set", cap) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if err := g.DropProcessCapabilityEffective(cap); err != nil { | 
					
						
							|  |  |  | 			return errors.Wrapf(err, "error removing %q from the effective capability set", cap) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if err := g.DropProcessCapabilityInheritable(cap); err != nil { | 
					
						
							|  |  |  | 			return errors.Wrapf(err, "error removing %q from the inheritable capability set", cap) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if err := g.DropProcessCapabilityPermitted(cap); err != nil { | 
					
						
							|  |  |  | 			return errors.Wrapf(err, "error removing %q from the permitted capability set", cap) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if err := g.DropProcessCapabilityAmbient(cap); err != nil { | 
					
						
							|  |  |  | 			return errors.Wrapf(err, "error removing %q from the ambient capability set", cap) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func setupCapabilities(g *generate.Generator, firstAdds, firstDrops, secondAdds, secondDrops []string) error { | 
					
						
							|  |  |  | 	g.ClearProcessCapabilities() | 
					
						
							|  |  |  | 	if err := setupCapAdd(g, util.DefaultCapabilities...); err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if err := setupCapAdd(g, firstAdds...); err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if err := setupCapDrop(g, firstDrops...); err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if err := setupCapAdd(g, secondAdds...); err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if err := setupCapDrop(g, secondDrops...); err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-08 07:11:43 +08:00
										 |  |  | func setupSeccomp(spec *specs.Spec, seccompProfilePath string) error { | 
					
						
							| 
									
										
										
										
											2018-06-05 05:35:19 +08:00
										 |  |  | 	switch seccompProfilePath { | 
					
						
							|  |  |  | 	case "unconfined": | 
					
						
							|  |  |  | 		spec.Linux.Seccomp = nil | 
					
						
							|  |  |  | 	case "": | 
					
						
							|  |  |  | 		seccompConfig, err := seccomp.GetDefaultProfile(spec) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return errors.Wrapf(err, "loading default seccomp profile failed") | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		spec.Linux.Seccomp = seccompConfig | 
					
						
							|  |  |  | 	default: | 
					
						
							|  |  |  | 		seccompProfile, err := ioutil.ReadFile(seccompProfilePath) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return errors.Wrapf(err, "opening seccomp profile (%s) failed", seccompProfilePath) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		seccompConfig, err := seccomp.LoadProfile(string(seccompProfile), spec) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return errors.Wrapf(err, "loading seccomp profile (%s) failed", seccompProfilePath) | 
					
						
							| 
									
										
										
										
											2018-03-08 07:11:43 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2018-06-05 05:35:19 +08:00
										 |  |  | 		spec.Linux.Seccomp = seccompConfig | 
					
						
							| 
									
										
										
										
											2018-03-08 07:11:43 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func setupApparmor(spec *specs.Spec, apparmorProfile string) error { | 
					
						
							|  |  |  | 	spec.Process.ApparmorProfile = apparmorProfile | 
					
						
							|  |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-06-12 02:15:00 +08:00
										 |  |  | func setupTerminal(g *generate.Generator, terminalPolicy TerminalPolicy, terminalSize *specs.Box) { | 
					
						
							| 
									
										
										
										
											2018-03-08 07:11:43 +08:00
										 |  |  | 	switch terminalPolicy { | 
					
						
							|  |  |  | 	case DefaultTerminal: | 
					
						
							| 
									
										
										
										
											2018-06-12 02:15:00 +08:00
										 |  |  | 		onTerminal := terminal.IsTerminal(unix.Stdin) && terminal.IsTerminal(unix.Stdout) && terminal.IsTerminal(unix.Stderr) | 
					
						
							|  |  |  | 		if onTerminal { | 
					
						
							|  |  |  | 			logrus.Debugf("stdio is a terminal, defaulting to using a terminal") | 
					
						
							|  |  |  | 		} else { | 
					
						
							|  |  |  | 			logrus.Debugf("stdio is not a terminal, defaulting to not using a terminal") | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		g.SetProcessTerminal(onTerminal) | 
					
						
							| 
									
										
										
										
											2018-03-08 07:11:43 +08:00
										 |  |  | 	case WithTerminal: | 
					
						
							|  |  |  | 		g.SetProcessTerminal(true) | 
					
						
							|  |  |  | 	case WithoutTerminal: | 
					
						
							|  |  |  | 		g.SetProcessTerminal(false) | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-06-12 02:15:00 +08:00
										 |  |  | 	if terminalSize != nil { | 
					
						
							|  |  |  | 		g.SetProcessConsoleSize(terminalSize.Width, terminalSize.Height) | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-03-08 07:11:43 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
											  
											
												Run: use a temporary mount namespace
Move the thread that launches the runtime helper into its own OS thread,
have it create its own mount namespace, and bind mount anything that we
want to eventually bind mount into the container, into a subdirectory of
the bundle directory, before running the helper.
When deciding what to bind mount in, make the volumes specified by the
user our highest priority, in case they've been specified in order to
override our default settings for a given location.  This required
breaking up setupMounts() to keep the complexity tests from complaining.
When we use a user namespace with the host IPC namespace, bind mount
/dev/shm and /dev/mqueue instead of mounting fresh copies.
If we're told to use a user namespace with the host PID namespace,
return an error, because that doesn't work.
When we use a user namespace with the host network namespace, bind mount
/sys instead of mounting a fresh one.
When we use the host UTS namespace, don't try to set a hostname.
Signed-off-by: Nalin Dahyabhai <nalin@redhat.com>
Closes: #700
Approved by: rhatdan
											
										 
											2018-05-03 00:31:05 +08:00
										 |  |  | func setupNamespaces(g *generate.Generator, namespaceOptions NamespaceOptions, idmapOptions IDMappingOptions, policy NetworkConfigurationPolicy) (configureNetwork bool, configureNetworks []string, configureUTS bool, err error) { | 
					
						
							| 
									
										
										
										
											2018-03-08 07:11:43 +08:00
										 |  |  | 	// Set namespace options in the container configuration.
 | 
					
						
							| 
									
										
											  
											
												Run: use a temporary mount namespace
Move the thread that launches the runtime helper into its own OS thread,
have it create its own mount namespace, and bind mount anything that we
want to eventually bind mount into the container, into a subdirectory of
the bundle directory, before running the helper.
When deciding what to bind mount in, make the volumes specified by the
user our highest priority, in case they've been specified in order to
override our default settings for a given location.  This required
breaking up setupMounts() to keep the complexity tests from complaining.
When we use a user namespace with the host IPC namespace, bind mount
/dev/shm and /dev/mqueue instead of mounting fresh copies.
If we're told to use a user namespace with the host PID namespace,
return an error, because that doesn't work.
When we use a user namespace with the host network namespace, bind mount
/sys instead of mounting a fresh one.
When we use the host UTS namespace, don't try to set a hostname.
Signed-off-by: Nalin Dahyabhai <nalin@redhat.com>
Closes: #700
Approved by: rhatdan
											
										 
											2018-05-03 00:31:05 +08:00
										 |  |  | 	hostPidns := false | 
					
						
							| 
									
										
										
										
											2018-04-24 03:55:46 +08:00
										 |  |  | 	configureUserns := false | 
					
						
							| 
									
										
										
										
											2018-04-14 06:20:25 +08:00
										 |  |  | 	specifiedNetwork := false | 
					
						
							| 
									
										
										
										
											2018-03-08 07:11:43 +08:00
										 |  |  | 	for _, namespaceOption := range namespaceOptions { | 
					
						
							| 
									
										
										
										
											2018-04-24 03:55:46 +08:00
										 |  |  | 		switch namespaceOption.Name { | 
					
						
							|  |  |  | 		case string(specs.UserNamespace): | 
					
						
							|  |  |  | 			configureUserns = false | 
					
						
							|  |  |  | 			if !namespaceOption.Host && namespaceOption.Path == "" { | 
					
						
							|  |  |  | 				configureUserns = true | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
											  
											
												Run: use a temporary mount namespace
Move the thread that launches the runtime helper into its own OS thread,
have it create its own mount namespace, and bind mount anything that we
want to eventually bind mount into the container, into a subdirectory of
the bundle directory, before running the helper.
When deciding what to bind mount in, make the volumes specified by the
user our highest priority, in case they've been specified in order to
override our default settings for a given location.  This required
breaking up setupMounts() to keep the complexity tests from complaining.
When we use a user namespace with the host IPC namespace, bind mount
/dev/shm and /dev/mqueue instead of mounting fresh copies.
If we're told to use a user namespace with the host PID namespace,
return an error, because that doesn't work.
When we use a user namespace with the host network namespace, bind mount
/sys instead of mounting a fresh one.
When we use the host UTS namespace, don't try to set a hostname.
Signed-off-by: Nalin Dahyabhai <nalin@redhat.com>
Closes: #700
Approved by: rhatdan
											
										 
											2018-05-03 00:31:05 +08:00
										 |  |  | 		case string(specs.PIDNamespace): | 
					
						
							|  |  |  | 			hostPidns = namespaceOption.Host | 
					
						
							|  |  |  | 		case string(specs.NetworkNamespace): | 
					
						
							| 
									
										
										
										
											2018-04-14 06:20:25 +08:00
										 |  |  | 			specifiedNetwork = true | 
					
						
							|  |  |  | 			configureNetwork = false | 
					
						
							|  |  |  | 			if !namespaceOption.Host && (namespaceOption.Path == "" || !filepath.IsAbs(namespaceOption.Path)) { | 
					
						
							|  |  |  | 				if namespaceOption.Path != "" && !filepath.IsAbs(namespaceOption.Path) { | 
					
						
							|  |  |  | 					configureNetworks = strings.Split(namespaceOption.Path, ",") | 
					
						
							|  |  |  | 					namespaceOption.Path = "" | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				configureNetwork = (policy != NetworkDisabled) | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
											  
											
												Run: use a temporary mount namespace
Move the thread that launches the runtime helper into its own OS thread,
have it create its own mount namespace, and bind mount anything that we
want to eventually bind mount into the container, into a subdirectory of
the bundle directory, before running the helper.
When deciding what to bind mount in, make the volumes specified by the
user our highest priority, in case they've been specified in order to
override our default settings for a given location.  This required
breaking up setupMounts() to keep the complexity tests from complaining.
When we use a user namespace with the host IPC namespace, bind mount
/dev/shm and /dev/mqueue instead of mounting fresh copies.
If we're told to use a user namespace with the host PID namespace,
return an error, because that doesn't work.
When we use a user namespace with the host network namespace, bind mount
/sys instead of mounting a fresh one.
When we use the host UTS namespace, don't try to set a hostname.
Signed-off-by: Nalin Dahyabhai <nalin@redhat.com>
Closes: #700
Approved by: rhatdan
											
										 
											2018-05-03 00:31:05 +08:00
										 |  |  | 		case string(specs.UTSNamespace): | 
					
						
							|  |  |  | 			configureUTS = false | 
					
						
							|  |  |  | 			if !namespaceOption.Host && namespaceOption.Path == "" { | 
					
						
							|  |  |  | 				configureUTS = true | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2018-04-24 03:55:46 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2018-03-08 07:11:43 +08:00
										 |  |  | 		if namespaceOption.Host { | 
					
						
							|  |  |  | 			if err := g.RemoveLinuxNamespace(namespaceOption.Name); err != nil { | 
					
						
							| 
									
										
											  
											
												Run: use a temporary mount namespace
Move the thread that launches the runtime helper into its own OS thread,
have it create its own mount namespace, and bind mount anything that we
want to eventually bind mount into the container, into a subdirectory of
the bundle directory, before running the helper.
When deciding what to bind mount in, make the volumes specified by the
user our highest priority, in case they've been specified in order to
override our default settings for a given location.  This required
breaking up setupMounts() to keep the complexity tests from complaining.
When we use a user namespace with the host IPC namespace, bind mount
/dev/shm and /dev/mqueue instead of mounting fresh copies.
If we're told to use a user namespace with the host PID namespace,
return an error, because that doesn't work.
When we use a user namespace with the host network namespace, bind mount
/sys instead of mounting a fresh one.
When we use the host UTS namespace, don't try to set a hostname.
Signed-off-by: Nalin Dahyabhai <nalin@redhat.com>
Closes: #700
Approved by: rhatdan
											
										 
											2018-05-03 00:31:05 +08:00
										 |  |  | 				return false, nil, false, errors.Wrapf(err, "error removing %q namespace for run", namespaceOption.Name) | 
					
						
							| 
									
										
										
										
											2018-03-08 07:11:43 +08:00
										 |  |  | 			} | 
					
						
							|  |  |  | 		} else if err := g.AddOrReplaceLinuxNamespace(namespaceOption.Name, namespaceOption.Path); err != nil { | 
					
						
							|  |  |  | 			if namespaceOption.Path == "" { | 
					
						
							| 
									
										
											  
											
												Run: use a temporary mount namespace
Move the thread that launches the runtime helper into its own OS thread,
have it create its own mount namespace, and bind mount anything that we
want to eventually bind mount into the container, into a subdirectory of
the bundle directory, before running the helper.
When deciding what to bind mount in, make the volumes specified by the
user our highest priority, in case they've been specified in order to
override our default settings for a given location.  This required
breaking up setupMounts() to keep the complexity tests from complaining.
When we use a user namespace with the host IPC namespace, bind mount
/dev/shm and /dev/mqueue instead of mounting fresh copies.
If we're told to use a user namespace with the host PID namespace,
return an error, because that doesn't work.
When we use a user namespace with the host network namespace, bind mount
/sys instead of mounting a fresh one.
When we use the host UTS namespace, don't try to set a hostname.
Signed-off-by: Nalin Dahyabhai <nalin@redhat.com>
Closes: #700
Approved by: rhatdan
											
										 
											2018-05-03 00:31:05 +08:00
										 |  |  | 				return false, nil, false, errors.Wrapf(err, "error adding new %q namespace for run", namespaceOption.Name) | 
					
						
							| 
									
										
										
										
											2018-03-08 07:11:43 +08:00
										 |  |  | 			} | 
					
						
							| 
									
										
											  
											
												Run: use a temporary mount namespace
Move the thread that launches the runtime helper into its own OS thread,
have it create its own mount namespace, and bind mount anything that we
want to eventually bind mount into the container, into a subdirectory of
the bundle directory, before running the helper.
When deciding what to bind mount in, make the volumes specified by the
user our highest priority, in case they've been specified in order to
override our default settings for a given location.  This required
breaking up setupMounts() to keep the complexity tests from complaining.
When we use a user namespace with the host IPC namespace, bind mount
/dev/shm and /dev/mqueue instead of mounting fresh copies.
If we're told to use a user namespace with the host PID namespace,
return an error, because that doesn't work.
When we use a user namespace with the host network namespace, bind mount
/sys instead of mounting a fresh one.
When we use the host UTS namespace, don't try to set a hostname.
Signed-off-by: Nalin Dahyabhai <nalin@redhat.com>
Closes: #700
Approved by: rhatdan
											
										 
											2018-05-03 00:31:05 +08:00
										 |  |  | 			return false, nil, false, errors.Wrapf(err, "error adding %q namespace %q for run", namespaceOption.Name, namespaceOption.Path) | 
					
						
							| 
									
										
										
										
											2018-03-08 07:11:43 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	// If we've got mappings, we're going to have to create a user namespace.
 | 
					
						
							| 
									
										
										
										
											2018-04-24 03:55:46 +08:00
										 |  |  | 	if len(idmapOptions.UIDMap) > 0 || len(idmapOptions.GIDMap) > 0 || configureUserns { | 
					
						
							| 
									
										
											  
											
												Run: use a temporary mount namespace
Move the thread that launches the runtime helper into its own OS thread,
have it create its own mount namespace, and bind mount anything that we
want to eventually bind mount into the container, into a subdirectory of
the bundle directory, before running the helper.
When deciding what to bind mount in, make the volumes specified by the
user our highest priority, in case they've been specified in order to
override our default settings for a given location.  This required
breaking up setupMounts() to keep the complexity tests from complaining.
When we use a user namespace with the host IPC namespace, bind mount
/dev/shm and /dev/mqueue instead of mounting fresh copies.
If we're told to use a user namespace with the host PID namespace,
return an error, because that doesn't work.
When we use a user namespace with the host network namespace, bind mount
/sys instead of mounting a fresh one.
When we use the host UTS namespace, don't try to set a hostname.
Signed-off-by: Nalin Dahyabhai <nalin@redhat.com>
Closes: #700
Approved by: rhatdan
											
										 
											2018-05-03 00:31:05 +08:00
										 |  |  | 		if hostPidns { | 
					
						
							|  |  |  | 			return false, nil, false, errors.Wrapf(err, "unable to mix host PID namespace with user namespace") | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2018-03-08 07:11:43 +08:00
										 |  |  | 		if err := g.AddOrReplaceLinuxNamespace(specs.UserNamespace, ""); err != nil { | 
					
						
							| 
									
										
											  
											
												Run: use a temporary mount namespace
Move the thread that launches the runtime helper into its own OS thread,
have it create its own mount namespace, and bind mount anything that we
want to eventually bind mount into the container, into a subdirectory of
the bundle directory, before running the helper.
When deciding what to bind mount in, make the volumes specified by the
user our highest priority, in case they've been specified in order to
override our default settings for a given location.  This required
breaking up setupMounts() to keep the complexity tests from complaining.
When we use a user namespace with the host IPC namespace, bind mount
/dev/shm and /dev/mqueue instead of mounting fresh copies.
If we're told to use a user namespace with the host PID namespace,
return an error, because that doesn't work.
When we use a user namespace with the host network namespace, bind mount
/sys instead of mounting a fresh one.
When we use the host UTS namespace, don't try to set a hostname.
Signed-off-by: Nalin Dahyabhai <nalin@redhat.com>
Closes: #700
Approved by: rhatdan
											
										 
											2018-05-03 00:31:05 +08:00
										 |  |  | 			return false, nil, false, errors.Wrapf(err, "error adding new %q namespace for run", string(specs.UserNamespace)) | 
					
						
							| 
									
										
										
										
											2018-03-08 07:11:43 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2018-06-15 06:39:19 +08:00
										 |  |  | 		hostUidmap, hostGidmap, err := util.GetHostIDMappings("") | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return false, nil, false, err | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2018-03-08 07:11:43 +08:00
										 |  |  | 		for _, m := range idmapOptions.UIDMap { | 
					
						
							|  |  |  | 			g.AddLinuxUIDMapping(m.HostID, m.ContainerID, m.Size) | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2018-04-24 03:55:46 +08:00
										 |  |  | 		if len(idmapOptions.UIDMap) == 0 { | 
					
						
							| 
									
										
										
										
											2018-06-15 06:39:19 +08:00
										 |  |  | 			for _, m := range hostUidmap { | 
					
						
							| 
									
										
										
										
											2018-04-24 03:55:46 +08:00
										 |  |  | 				g.AddLinuxUIDMapping(m.ContainerID, m.ContainerID, m.Size) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2018-03-08 07:11:43 +08:00
										 |  |  | 		for _, m := range idmapOptions.GIDMap { | 
					
						
							|  |  |  | 			g.AddLinuxGIDMapping(m.HostID, m.ContainerID, m.Size) | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2018-04-24 03:55:46 +08:00
										 |  |  | 		if len(idmapOptions.GIDMap) == 0 { | 
					
						
							| 
									
										
										
										
											2018-06-15 06:39:19 +08:00
										 |  |  | 			for _, m := range hostGidmap { | 
					
						
							| 
									
										
										
										
											2018-04-24 03:55:46 +08:00
										 |  |  | 				g.AddLinuxGIDMapping(m.ContainerID, m.ContainerID, m.Size) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2018-04-14 06:20:25 +08:00
										 |  |  | 		if !specifiedNetwork { | 
					
						
							| 
									
										
										
										
											2018-03-08 07:11:43 +08:00
										 |  |  | 			if err := g.AddOrReplaceLinuxNamespace(specs.NetworkNamespace, ""); err != nil { | 
					
						
							| 
									
										
											  
											
												Run: use a temporary mount namespace
Move the thread that launches the runtime helper into its own OS thread,
have it create its own mount namespace, and bind mount anything that we
want to eventually bind mount into the container, into a subdirectory of
the bundle directory, before running the helper.
When deciding what to bind mount in, make the volumes specified by the
user our highest priority, in case they've been specified in order to
override our default settings for a given location.  This required
breaking up setupMounts() to keep the complexity tests from complaining.
When we use a user namespace with the host IPC namespace, bind mount
/dev/shm and /dev/mqueue instead of mounting fresh copies.
If we're told to use a user namespace with the host PID namespace,
return an error, because that doesn't work.
When we use a user namespace with the host network namespace, bind mount
/sys instead of mounting a fresh one.
When we use the host UTS namespace, don't try to set a hostname.
Signed-off-by: Nalin Dahyabhai <nalin@redhat.com>
Closes: #700
Approved by: rhatdan
											
										 
											2018-05-03 00:31:05 +08:00
										 |  |  | 				return false, nil, false, errors.Wrapf(err, "error adding new %q namespace for run", string(specs.NetworkNamespace)) | 
					
						
							| 
									
										
										
										
											2018-03-08 07:11:43 +08:00
										 |  |  | 			} | 
					
						
							| 
									
										
										
										
											2018-04-14 06:20:25 +08:00
										 |  |  | 			configureNetwork = (policy != NetworkDisabled) | 
					
						
							| 
									
										
										
										
											2018-03-08 07:11:43 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		if err := g.RemoveLinuxNamespace(specs.UserNamespace); err != nil { | 
					
						
							| 
									
										
											  
											
												Run: use a temporary mount namespace
Move the thread that launches the runtime helper into its own OS thread,
have it create its own mount namespace, and bind mount anything that we
want to eventually bind mount into the container, into a subdirectory of
the bundle directory, before running the helper.
When deciding what to bind mount in, make the volumes specified by the
user our highest priority, in case they've been specified in order to
override our default settings for a given location.  This required
breaking up setupMounts() to keep the complexity tests from complaining.
When we use a user namespace with the host IPC namespace, bind mount
/dev/shm and /dev/mqueue instead of mounting fresh copies.
If we're told to use a user namespace with the host PID namespace,
return an error, because that doesn't work.
When we use a user namespace with the host network namespace, bind mount
/sys instead of mounting a fresh one.
When we use the host UTS namespace, don't try to set a hostname.
Signed-off-by: Nalin Dahyabhai <nalin@redhat.com>
Closes: #700
Approved by: rhatdan
											
										 
											2018-05-03 00:31:05 +08:00
										 |  |  | 			return false, nil, false, errors.Wrapf(err, "error removing %q namespace for run", string(specs.UserNamespace)) | 
					
						
							| 
									
										
										
										
											2018-03-08 07:11:43 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2018-04-14 06:20:25 +08:00
										 |  |  | 		if !specifiedNetwork { | 
					
						
							| 
									
										
										
										
											2018-03-08 07:11:43 +08:00
										 |  |  | 			if err := g.RemoveLinuxNamespace(specs.NetworkNamespace); err != nil { | 
					
						
							| 
									
										
											  
											
												Run: use a temporary mount namespace
Move the thread that launches the runtime helper into its own OS thread,
have it create its own mount namespace, and bind mount anything that we
want to eventually bind mount into the container, into a subdirectory of
the bundle directory, before running the helper.
When deciding what to bind mount in, make the volumes specified by the
user our highest priority, in case they've been specified in order to
override our default settings for a given location.  This required
breaking up setupMounts() to keep the complexity tests from complaining.
When we use a user namespace with the host IPC namespace, bind mount
/dev/shm and /dev/mqueue instead of mounting fresh copies.
If we're told to use a user namespace with the host PID namespace,
return an error, because that doesn't work.
When we use a user namespace with the host network namespace, bind mount
/sys instead of mounting a fresh one.
When we use the host UTS namespace, don't try to set a hostname.
Signed-off-by: Nalin Dahyabhai <nalin@redhat.com>
Closes: #700
Approved by: rhatdan
											
										 
											2018-05-03 00:31:05 +08:00
										 |  |  | 				return false, nil, false, errors.Wrapf(err, "error removing %q namespace for run", string(specs.NetworkNamespace)) | 
					
						
							| 
									
										
										
										
											2018-03-08 07:11:43 +08:00
										 |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
											  
											
												Run: use a temporary mount namespace
Move the thread that launches the runtime helper into its own OS thread,
have it create its own mount namespace, and bind mount anything that we
want to eventually bind mount into the container, into a subdirectory of
the bundle directory, before running the helper.
When deciding what to bind mount in, make the volumes specified by the
user our highest priority, in case they've been specified in order to
override our default settings for a given location.  This required
breaking up setupMounts() to keep the complexity tests from complaining.
When we use a user namespace with the host IPC namespace, bind mount
/dev/shm and /dev/mqueue instead of mounting fresh copies.
If we're told to use a user namespace with the host PID namespace,
return an error, because that doesn't work.
When we use a user namespace with the host network namespace, bind mount
/sys instead of mounting a fresh one.
When we use the host UTS namespace, don't try to set a hostname.
Signed-off-by: Nalin Dahyabhai <nalin@redhat.com>
Closes: #700
Approved by: rhatdan
											
										 
											2018-05-03 00:31:05 +08:00
										 |  |  | 	return configureNetwork, configureNetworks, configureUTS, nil | 
					
						
							| 
									
										
										
										
											2018-03-08 07:11:43 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-02-14 05:12:02 +08:00
										 |  |  | // Run runs the specified command in the container's root filesystem.
 | 
					
						
							|  |  |  | func (b *Builder) Run(command []string, options RunOptions) error { | 
					
						
							| 
									
										
										
										
											2017-03-28 02:46:35 +08:00
										 |  |  | 	var user specs.User | 
					
						
							| 
									
										
										
										
											2018-06-02 02:54:45 +08:00
										 |  |  | 	p, err := ioutil.TempDir("", Package) | 
					
						
							| 
									
										
										
										
											2018-06-02 03:09:40 +08:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	// On some hosts like AH, /tmp is a symlink and we need an
 | 
					
						
							|  |  |  | 	// absolute path.
 | 
					
						
							|  |  |  | 	path, err := filepath.EvalSymlinks(p) | 
					
						
							| 
									
										
										
										
											2017-02-14 05:12:02 +08:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	logrus.Debugf("using %q to hold bundle data", path) | 
					
						
							|  |  |  | 	defer func() { | 
					
						
							|  |  |  | 		if err2 := os.RemoveAll(path); err2 != nil { | 
					
						
							|  |  |  | 			logrus.Errorf("error removing %q: %v", path, err2) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	}() | 
					
						
							| 
									
										
										
										
											2018-03-08 07:11:43 +08:00
										 |  |  | 	gp := generate.New() | 
					
						
							|  |  |  | 	g := &gp | 
					
						
							| 
									
										
										
										
											2017-02-14 05:12:02 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
											
												Maintain multiple working container configs
Maintain the container configuration in multiple formats in the Buildah
object, initializing one based on the other, depending on which format
the source image used for its configuration.
Replace directly manipulated fields in the Buildah object (Annotations,
CreatedBy, OS, Architecture, Maintainer, User, Workdir, Env, Cmd,
Entrypoint, Expose, Labels, and Volumes) with accessor functions which
update both configurations and which read from whichever one we consider
to be authoritative.  Drop Args because we weren't using them.
Signed-off-by: Nalin Dahyabhai <nalin@redhat.com>
Closes: #102
Approved by: rhatdan
											
										 
											2017-05-16 23:08:52 +08:00
										 |  |  | 	for _, envSpec := range append(b.Env(), options.Env...) { | 
					
						
							| 
									
										
										
										
											2017-02-14 05:12:02 +08:00
										 |  |  | 		env := strings.SplitN(envSpec, "=", 2) | 
					
						
							|  |  |  | 		if len(env) > 1 { | 
					
						
							|  |  |  | 			g.AddProcessEnv(env[0], env[1]) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-02-14 03:58:56 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-27 07:43:36 +08:00
										 |  |  | 	if b.CommonBuildOpts == nil { | 
					
						
							|  |  |  | 		return errors.Errorf("Invalid format on container you must recreate the container") | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-08 07:11:43 +08:00
										 |  |  | 	if err := addCommonOptsToSpec(b.CommonBuildOpts, g); err != nil { | 
					
						
							| 
									
										
										
										
											2018-02-14 03:58:56 +08:00
										 |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-02-14 05:12:02 +08:00
										 |  |  | 	if len(command) > 0 { | 
					
						
							| 
									
										
										
										
											2018-04-24 06:15:58 +08:00
										 |  |  | 		g.SetProcessArgs(command) | 
					
						
							| 
									
										
										
										
											2017-06-23 23:53:51 +08:00
										 |  |  | 	} else { | 
					
						
							| 
									
										
										
										
											2018-05-03 05:00:44 +08:00
										 |  |  | 		g.SetProcessArgs(nil) | 
					
						
							| 
									
										
										
										
											2017-02-14 05:12:02 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-03-28 02:46:35 +08:00
										 |  |  | 	if options.WorkingDir != "" { | 
					
						
							|  |  |  | 		g.SetProcessCwd(options.WorkingDir) | 
					
						
							| 
									
										
										
											
												Maintain multiple working container configs
Maintain the container configuration in multiple formats in the Buildah
object, initializing one based on the other, depending on which format
the source image used for its configuration.
Replace directly manipulated fields in the Buildah object (Annotations,
CreatedBy, OS, Architecture, Maintainer, User, Workdir, Env, Cmd,
Entrypoint, Expose, Labels, and Volumes) with accessor functions which
update both configurations and which read from whichever one we consider
to be authoritative.  Drop Args because we weren't using them.
Signed-off-by: Nalin Dahyabhai <nalin@redhat.com>
Closes: #102
Approved by: rhatdan
											
										 
											2017-05-16 23:08:52 +08:00
										 |  |  | 	} else if b.WorkDir() != "" { | 
					
						
							|  |  |  | 		g.SetProcessCwd(b.WorkDir()) | 
					
						
							| 
									
										
										
										
											2017-02-14 05:12:02 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-10-20 05:47:15 +08:00
										 |  |  | 	g.SetProcessSelinuxLabel(b.ProcessLabel) | 
					
						
							|  |  |  | 	g.SetLinuxMountLabel(b.MountLabel) | 
					
						
							|  |  |  | 	mountPoint, err := b.Mount(b.MountLabel) | 
					
						
							| 
									
										
										
										
											2017-02-14 05:12:02 +08:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	defer func() { | 
					
						
							|  |  |  | 		if err2 := b.Unmount(); err2 != nil { | 
					
						
							|  |  |  | 			logrus.Errorf("error unmounting container: %v", err2) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	}() | 
					
						
							| 
									
										
										
										
											2017-11-22 22:42:16 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-08 07:11:43 +08:00
										 |  |  | 	setupMaskedPaths(g) | 
					
						
							|  |  |  | 	setupReadOnlyPaths(g) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-02-14 05:12:02 +08:00
										 |  |  | 	g.SetRootPath(mountPoint) | 
					
						
							| 
									
										
										
										
											2018-03-08 07:11:43 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-06-12 02:15:00 +08:00
										 |  |  | 	setupTerminal(g, options.Terminal, options.TerminalSize) | 
					
						
							| 
									
										
										
										
											2018-03-08 07:11:43 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	namespaceOptions := DefaultNamespaceOptions() | 
					
						
							|  |  |  | 	namespaceOptions.AddOrReplace(b.NamespaceOptions...) | 
					
						
							|  |  |  | 	namespaceOptions.AddOrReplace(options.NamespaceOptions...) | 
					
						
							| 
									
										
										
										
											2018-04-14 06:20:25 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	networkPolicy := options.ConfigureNetwork | 
					
						
							|  |  |  | 	if networkPolicy == NetworkDefault { | 
					
						
							|  |  |  | 		networkPolicy = b.ConfigureNetwork | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
											  
											
												Run: use a temporary mount namespace
Move the thread that launches the runtime helper into its own OS thread,
have it create its own mount namespace, and bind mount anything that we
want to eventually bind mount into the container, into a subdirectory of
the bundle directory, before running the helper.
When deciding what to bind mount in, make the volumes specified by the
user our highest priority, in case they've been specified in order to
override our default settings for a given location.  This required
breaking up setupMounts() to keep the complexity tests from complaining.
When we use a user namespace with the host IPC namespace, bind mount
/dev/shm and /dev/mqueue instead of mounting fresh copies.
If we're told to use a user namespace with the host PID namespace,
return an error, because that doesn't work.
When we use a user namespace with the host network namespace, bind mount
/sys instead of mounting a fresh one.
When we use the host UTS namespace, don't try to set a hostname.
Signed-off-by: Nalin Dahyabhai <nalin@redhat.com>
Closes: #700
Approved by: rhatdan
											
										 
											2018-05-03 00:31:05 +08:00
										 |  |  | 	configureNetwork, configureNetworks, configureUTS, err := setupNamespaces(g, namespaceOptions, b.IDMappingOptions, networkPolicy) | 
					
						
							| 
									
										
										
										
											2018-04-14 06:20:25 +08:00
										 |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2018-03-08 07:11:43 +08:00
										 |  |  | 		return err | 
					
						
							| 
									
										
										
										
											2017-03-28 02:46:35 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-04-24 03:55:46 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
											  
											
												Run: use a temporary mount namespace
Move the thread that launches the runtime helper into its own OS thread,
have it create its own mount namespace, and bind mount anything that we
want to eventually bind mount into the container, into a subdirectory of
the bundle directory, before running the helper.
When deciding what to bind mount in, make the volumes specified by the
user our highest priority, in case they've been specified in order to
override our default settings for a given location.  This required
breaking up setupMounts() to keep the complexity tests from complaining.
When we use a user namespace with the host IPC namespace, bind mount
/dev/shm and /dev/mqueue instead of mounting fresh copies.
If we're told to use a user namespace with the host PID namespace,
return an error, because that doesn't work.
When we use a user namespace with the host network namespace, bind mount
/sys instead of mounting a fresh one.
When we use the host UTS namespace, don't try to set a hostname.
Signed-off-by: Nalin Dahyabhai <nalin@redhat.com>
Closes: #700
Approved by: rhatdan
											
										 
											2018-05-03 00:31:05 +08:00
										 |  |  | 	if configureUTS { | 
					
						
							|  |  |  | 		if options.Hostname != "" { | 
					
						
							|  |  |  | 			g.SetHostname(options.Hostname) | 
					
						
							|  |  |  | 		} else if b.Hostname() != "" { | 
					
						
							|  |  |  | 			g.SetHostname(b.Hostname()) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		g.SetHostname("") | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-06-05 05:36:26 +08:00
										 |  |  | 	// Set the user UID/GID/supplemental group list/capabilities lists.
 | 
					
						
							| 
									
										
										
										
											2018-03-08 07:11:43 +08:00
										 |  |  | 	if user, err = b.user(mountPoint, options.User); err != nil { | 
					
						
							| 
									
										
										
										
											2017-04-05 05:31:02 +08:00
										 |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-06-05 05:36:26 +08:00
										 |  |  | 	if err = setupCapabilities(g, b.AddCapabilities, b.DropCapabilities, options.AddCapabilities, options.DropCapabilities); err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-04-05 05:31:02 +08:00
										 |  |  | 	g.SetProcessUID(user.UID) | 
					
						
							|  |  |  | 	g.SetProcessGID(user.GID) | 
					
						
							| 
									
										
										
										
											2018-05-05 04:16:36 +08:00
										 |  |  | 	for _, gid := range user.AdditionalGids { | 
					
						
							|  |  |  | 		g.AddProcessAdditionalGid(gid) | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-03-08 07:11:43 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// Now grab the spec from the generator.  Set the generator to nil so that future contributors
 | 
					
						
							|  |  |  | 	// will quickly be able to tell that they're supposed to be modifying the spec directly from here.
 | 
					
						
							| 
									
										
										
										
											2017-02-14 05:12:02 +08:00
										 |  |  | 	spec := g.Spec() | 
					
						
							| 
									
										
										
										
											2018-06-05 05:36:26 +08:00
										 |  |  | 	g = nil | 
					
						
							| 
									
										
										
										
											2018-06-08 04:21:36 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-06-05 05:36:26 +08:00
										 |  |  | 	// Remove capabilities if not running as root
 | 
					
						
							| 
									
										
										
										
											2018-06-08 04:21:36 +08:00
										 |  |  | 	if user.UID != 0 { | 
					
						
							|  |  |  | 		var caplist []string | 
					
						
							|  |  |  | 		spec.Process.Capabilities.Permitted = caplist | 
					
						
							|  |  |  | 		spec.Process.Capabilities.Inheritable = caplist | 
					
						
							|  |  |  | 		spec.Process.Capabilities.Effective = caplist | 
					
						
							|  |  |  | 		spec.Process.Capabilities.Ambient = caplist | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-06-05 05:36:26 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// Set the working directory, creating it if we must.
 | 
					
						
							| 
									
										
										
										
											2017-02-14 05:12:02 +08:00
										 |  |  | 	if spec.Process.Cwd == "" { | 
					
						
							|  |  |  | 		spec.Process.Cwd = DefaultWorkingDir | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-08-11 04:08:03 +08:00
										 |  |  | 	if err = os.MkdirAll(filepath.Join(mountPoint, spec.Process.Cwd), 0755); err != nil { | 
					
						
							|  |  |  | 		return errors.Wrapf(err, "error ensuring working directory %q exists", spec.Process.Cwd) | 
					
						
							| 
									
										
										
										
											2017-03-24 01:47:07 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-05-24 06:04:13 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-07 07:13:24 +08:00
										 |  |  | 	// Set the apparmor profile name.
 | 
					
						
							| 
									
										
										
										
											2018-03-08 07:11:43 +08:00
										 |  |  | 	if err = setupApparmor(spec, b.CommonBuildOpts.ApparmorProfile); err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-02-14 03:58:56 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-06-05 05:35:19 +08:00
										 |  |  | 	// Set the seccomp configuration using the specified profile name.  Some syscalls are
 | 
					
						
							|  |  |  | 	// allowed if certain capabilities are to be granted (example: CAP_SYS_CHROOT and chroot),
 | 
					
						
							|  |  |  | 	// so we sorted out the capabilities lists first.
 | 
					
						
							| 
									
										
										
										
											2018-03-08 07:11:43 +08:00
										 |  |  | 	if err = setupSeccomp(spec, b.CommonBuildOpts.SeccompProfilePath); err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							| 
									
										
										
										
											2018-02-14 03:58:56 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-05-19 04:00:58 +08:00
										 |  |  | 	hostFile, err := b.addNetworkConfig(path, "/etc/hosts") | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	resolvFile, err := b.addNetworkConfig(path, "/etc/resolv.conf") | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-02-14 03:58:56 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-05-19 04:00:58 +08:00
										 |  |  | 	if err := addHostsToFile(b.CommonBuildOpts.AddHost, hostFile); err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	bindFiles := map[string]string{ | 
					
						
							|  |  |  | 		"/etc/hosts":       hostFile, | 
					
						
							|  |  |  | 		"/etc/resolv.conf": resolvFile, | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-06-07 23:25:47 +08:00
										 |  |  | 	err = b.setupMounts(mountPoint, spec, path, options.Mounts, bindFiles, b.Volumes(), b.CommonBuildOpts.Volumes, b.CommonBuildOpts.ShmSize, append(b.NamespaceOptions, options.NamespaceOptions...)) | 
					
						
							| 
									
										
										
										
											2017-05-24 06:04:13 +08:00
										 |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2017-06-02 03:23:02 +08:00
										 |  |  | 		return errors.Wrapf(err, "error resolving mountpoints for container") | 
					
						
							| 
									
										
										
										
											2017-02-18 02:58:34 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-04-14 06:20:25 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	if options.CNIConfigDir == "" { | 
					
						
							|  |  |  | 		options.CNIConfigDir = b.CNIConfigDir | 
					
						
							|  |  |  | 		if b.CNIConfigDir == "" { | 
					
						
							|  |  |  | 			options.CNIConfigDir = util.DefaultCNIConfigDir | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if options.CNIPluginPath == "" { | 
					
						
							|  |  |  | 		options.CNIPluginPath = b.CNIPluginPath | 
					
						
							|  |  |  | 		if b.CNIPluginPath == "" { | 
					
						
							|  |  |  | 			options.CNIPluginPath = util.DefaultCNIPluginPath | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return b.runUsingRuntimeSubproc(options, configureNetwork, configureNetworks, spec, mountPoint, path, Package+"-"+filepath.Base(path)) | 
					
						
							| 
									
										
										
										
											2018-03-21 23:19:47 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-21 23:19:47 +08:00
										 |  |  | type runUsingRuntimeSubprocOptions struct { | 
					
						
							| 
									
										
										
										
											2018-04-14 06:20:25 +08:00
										 |  |  | 	Options           RunOptions | 
					
						
							|  |  |  | 	Spec              *specs.Spec | 
					
						
							|  |  |  | 	RootPath          string | 
					
						
							|  |  |  | 	BundlePath        string | 
					
						
							|  |  |  | 	ConfigureNetwork  bool | 
					
						
							|  |  |  | 	ConfigureNetworks []string | 
					
						
							|  |  |  | 	ContainerName     string | 
					
						
							| 
									
										
										
										
											2018-03-21 23:19:47 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-14 06:20:25 +08:00
										 |  |  | func (b *Builder) runUsingRuntimeSubproc(options RunOptions, configureNetwork bool, configureNetworks []string, spec *specs.Spec, rootPath, bundlePath, containerName string) (err error) { | 
					
						
							| 
									
										
										
										
											2018-03-21 23:19:47 +08:00
										 |  |  | 	var confwg sync.WaitGroup | 
					
						
							|  |  |  | 	config, conferr := json.Marshal(runUsingRuntimeSubprocOptions{ | 
					
						
							| 
									
										
										
										
											2018-04-14 06:20:25 +08:00
										 |  |  | 		Options:           options, | 
					
						
							|  |  |  | 		Spec:              spec, | 
					
						
							|  |  |  | 		RootPath:          rootPath, | 
					
						
							|  |  |  | 		BundlePath:        bundlePath, | 
					
						
							|  |  |  | 		ConfigureNetwork:  configureNetwork, | 
					
						
							|  |  |  | 		ConfigureNetworks: configureNetworks, | 
					
						
							|  |  |  | 		ContainerName:     containerName, | 
					
						
							| 
									
										
										
										
											2018-03-21 23:19:47 +08:00
										 |  |  | 	}) | 
					
						
							|  |  |  | 	if conferr != nil { | 
					
						
							|  |  |  | 		return errors.Wrapf(conferr, "error encoding configuration for %q", runUsingRuntimeCommand) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	cmd := reexec.Command(runUsingRuntimeCommand) | 
					
						
							|  |  |  | 	cmd.Dir = bundlePath | 
					
						
							| 
									
										
										
										
											2018-06-06 01:53:39 +08:00
										 |  |  | 	cmd.Stdin = options.Stdin | 
					
						
							|  |  |  | 	if cmd.Stdin == nil { | 
					
						
							|  |  |  | 		cmd.Stdin = os.Stdin | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	cmd.Stdout = options.Stdout | 
					
						
							|  |  |  | 	if cmd.Stdout == nil { | 
					
						
							|  |  |  | 		cmd.Stdout = os.Stdout | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	cmd.Stderr = options.Stderr | 
					
						
							|  |  |  | 	if cmd.Stderr == nil { | 
					
						
							|  |  |  | 		cmd.Stderr = os.Stderr | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-03-21 23:19:47 +08:00
										 |  |  | 	cmd.Env = append(os.Environ(), fmt.Sprintf("LOGLEVEL=%d", logrus.GetLevel())) | 
					
						
							|  |  |  | 	preader, pwriter, err := os.Pipe() | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return errors.Wrapf(err, "error creating configuration pipe") | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	confwg.Add(1) | 
					
						
							|  |  |  | 	go func() { | 
					
						
							|  |  |  | 		_, conferr = io.Copy(pwriter, bytes.NewReader(config)) | 
					
						
							|  |  |  | 		confwg.Done() | 
					
						
							|  |  |  | 	}() | 
					
						
							|  |  |  | 	cmd.ExtraFiles = append([]*os.File{preader}, cmd.ExtraFiles...) | 
					
						
							|  |  |  | 	defer preader.Close() | 
					
						
							|  |  |  | 	defer pwriter.Close() | 
					
						
							|  |  |  | 	err = cmd.Run() | 
					
						
							|  |  |  | 	confwg.Wait() | 
					
						
							|  |  |  | 	if err == nil { | 
					
						
							|  |  |  | 		return conferr | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return err | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func init() { | 
					
						
							|  |  |  | 	reexec.Register(runUsingRuntimeCommand, runUsingRuntimeMain) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func runUsingRuntimeMain() { | 
					
						
							|  |  |  | 	var options runUsingRuntimeSubprocOptions | 
					
						
							|  |  |  | 	// Set logging.
 | 
					
						
							|  |  |  | 	if level := os.Getenv("LOGLEVEL"); level != "" { | 
					
						
							|  |  |  | 		if ll, err := strconv.Atoi(level); err == nil { | 
					
						
							|  |  |  | 			logrus.SetLevel(logrus.Level(ll)) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	// Unpack our configuration.
 | 
					
						
							|  |  |  | 	confPipe := os.NewFile(3, "confpipe") | 
					
						
							|  |  |  | 	if confPipe == nil { | 
					
						
							|  |  |  | 		fmt.Fprintf(os.Stderr, "error reading options pipe\n") | 
					
						
							|  |  |  | 		os.Exit(1) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	defer confPipe.Close() | 
					
						
							|  |  |  | 	if err := json.NewDecoder(confPipe).Decode(&options); err != nil { | 
					
						
							|  |  |  | 		fmt.Fprintf(os.Stderr, "error decoding options: %v\n", err) | 
					
						
							|  |  |  | 		os.Exit(1) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	// Set ourselves up to read the container's exit status.  We're doing this in a child process
 | 
					
						
							|  |  |  | 	// so that we won't mess with the setting in a caller of the library.
 | 
					
						
							|  |  |  | 	if err := unix.Prctl(unix.PR_SET_CHILD_SUBREAPER, uintptr(1), 0, 0, 0); err != nil { | 
					
						
							|  |  |  | 		fmt.Fprintf(os.Stderr, "prctl(PR_SET_CHILD_SUBREAPER, 1): %v\n", err) | 
					
						
							|  |  |  | 		os.Exit(1) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	// Run the container, start to finish.
 | 
					
						
							| 
									
										
										
										
											2018-04-14 06:20:25 +08:00
										 |  |  | 	status, err := runUsingRuntime(options.Options, options.ConfigureNetwork, options.ConfigureNetworks, options.Spec, options.RootPath, options.BundlePath, options.ContainerName) | 
					
						
							| 
									
										
										
										
											2018-03-21 23:19:47 +08:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		fmt.Fprintf(os.Stderr, "error running container: %v\n", err) | 
					
						
							|  |  |  | 		os.Exit(1) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	// Pass the container's exit status back to the caller by exiting with the same status.
 | 
					
						
							|  |  |  | 	if status.Exited() { | 
					
						
							|  |  |  | 		os.Exit(status.ExitStatus()) | 
					
						
							|  |  |  | 	} else if status.Signaled() { | 
					
						
							|  |  |  | 		fmt.Fprintf(os.Stderr, "container exited on %s\n", status.Signal()) | 
					
						
							|  |  |  | 		os.Exit(1) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	os.Exit(1) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-14 06:20:25 +08:00
										 |  |  | func runUsingRuntime(options RunOptions, configureNetwork bool, configureNetworks []string, spec *specs.Spec, rootPath, bundlePath, containerName string) (wstatus unix.WaitStatus, err error) { | 
					
						
							| 
									
										
										
										
											2018-06-07 23:25:47 +08:00
										 |  |  | 	// Lock the caller to a single OS-level thread.
 | 
					
						
							| 
									
										
											  
											
												Run: use a temporary mount namespace
Move the thread that launches the runtime helper into its own OS thread,
have it create its own mount namespace, and bind mount anything that we
want to eventually bind mount into the container, into a subdirectory of
the bundle directory, before running the helper.
When deciding what to bind mount in, make the volumes specified by the
user our highest priority, in case they've been specified in order to
override our default settings for a given location.  This required
breaking up setupMounts() to keep the complexity tests from complaining.
When we use a user namespace with the host IPC namespace, bind mount
/dev/shm and /dev/mqueue instead of mounting fresh copies.
If we're told to use a user namespace with the host PID namespace,
return an error, because that doesn't work.
When we use a user namespace with the host network namespace, bind mount
/sys instead of mounting a fresh one.
When we use the host UTS namespace, don't try to set a hostname.
Signed-off-by: Nalin Dahyabhai <nalin@redhat.com>
Closes: #700
Approved by: rhatdan
											
										 
											2018-05-03 00:31:05 +08:00
										 |  |  | 	runtime.LockOSThread() | 
					
						
							| 
									
										
										
										
											2018-06-07 23:25:47 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// Set up bind mounts for things that a namespaced user might not be able to get to directly.
 | 
					
						
							|  |  |  | 	unmountAll, err := bind.SetupIntermediateMountNamespace(spec, bundlePath) | 
					
						
							| 
									
										
											  
											
												Run: use a temporary mount namespace
Move the thread that launches the runtime helper into its own OS thread,
have it create its own mount namespace, and bind mount anything that we
want to eventually bind mount into the container, into a subdirectory of
the bundle directory, before running the helper.
When deciding what to bind mount in, make the volumes specified by the
user our highest priority, in case they've been specified in order to
override our default settings for a given location.  This required
breaking up setupMounts() to keep the complexity tests from complaining.
When we use a user namespace with the host IPC namespace, bind mount
/dev/shm and /dev/mqueue instead of mounting fresh copies.
If we're told to use a user namespace with the host PID namespace,
return an error, because that doesn't work.
When we use a user namespace with the host network namespace, bind mount
/sys instead of mounting a fresh one.
When we use the host UTS namespace, don't try to set a hostname.
Signed-off-by: Nalin Dahyabhai <nalin@redhat.com>
Closes: #700
Approved by: rhatdan
											
										 
											2018-05-03 00:31:05 +08:00
										 |  |  | 	if unmountAll != nil { | 
					
						
							|  |  |  | 		defer func() { | 
					
						
							|  |  |  | 			if err := unmountAll(); err != nil { | 
					
						
							|  |  |  | 				logrus.Error(err) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		}() | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return 1, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-21 23:19:47 +08:00
										 |  |  | 	// Write the runtime configuration.
 | 
					
						
							| 
									
										
										
										
											2017-02-14 05:12:02 +08:00
										 |  |  | 	specbytes, err := json.Marshal(spec) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2018-03-21 23:19:47 +08:00
										 |  |  | 		return 1, err | 
					
						
							| 
									
										
										
										
											2017-02-14 05:12:02 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-03-21 23:19:47 +08:00
										 |  |  | 	if err = ioutils.AtomicWriteFile(filepath.Join(bundlePath, "config.json"), specbytes, 0600); err != nil { | 
					
						
							| 
									
										
										
										
											2018-03-21 23:19:47 +08:00
										 |  |  | 		return 1, errors.Wrapf(err, "error storing runtime configuration") | 
					
						
							| 
									
										
										
										
											2017-02-14 05:12:02 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-03-21 23:19:47 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-02-14 05:12:02 +08:00
										 |  |  | 	logrus.Debugf("config = %v", string(specbytes)) | 
					
						
							| 
									
										
										
										
											2018-03-21 23:19:47 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-21 23:19:47 +08:00
										 |  |  | 	// Decide which runtime to use.
 | 
					
						
							| 
									
										
										
										
											2017-02-18 02:54:49 +08:00
										 |  |  | 	runtime := options.Runtime | 
					
						
							|  |  |  | 	if runtime == "" { | 
					
						
							| 
									
										
										
										
											2018-06-13 05:21:08 +08:00
										 |  |  | 		runtime = util.Runtime() | 
					
						
							| 
									
										
										
										
											2017-02-18 02:54:49 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-03-21 23:19:47 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-21 23:19:47 +08:00
										 |  |  | 	// Default to not specifying a console socket location.
 | 
					
						
							| 
									
										
										
										
											2018-06-15 03:35:17 +08:00
										 |  |  | 	var moreCreateArgs []string | 
					
						
							| 
									
										
										
										
											2018-03-21 23:19:47 +08:00
										 |  |  | 	// Default to just passing down our stdio.
 | 
					
						
							| 
									
										
										
										
											2018-06-06 01:53:39 +08:00
										 |  |  | 	getCreateStdio := func() (io.ReadCloser, io.WriteCloser, io.WriteCloser) { | 
					
						
							|  |  |  | 		return os.Stdin, os.Stdout, os.Stderr | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-03-21 23:19:47 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// Figure out how we're doing stdio handling, and create pipes and sockets.
 | 
					
						
							|  |  |  | 	var stdio sync.WaitGroup | 
					
						
							|  |  |  | 	var consoleListener *net.UnixListener | 
					
						
							| 
									
										
										
										
											2018-06-01 01:34:32 +08:00
										 |  |  | 	var errorFds []int | 
					
						
							| 
									
										
										
										
											2018-03-21 23:19:47 +08:00
										 |  |  | 	stdioPipe := make([][]int, 3) | 
					
						
							|  |  |  | 	copyConsole := false | 
					
						
							| 
									
										
										
										
											2018-06-15 04:41:40 +08:00
										 |  |  | 	copyPipes := false | 
					
						
							| 
									
										
										
										
											2018-03-21 23:19:47 +08:00
										 |  |  | 	finishCopy := make([]int, 2) | 
					
						
							|  |  |  | 	if err = unix.Pipe(finishCopy); err != nil { | 
					
						
							|  |  |  | 		return 1, errors.Wrapf(err, "error creating pipe for notifying to stop stdio") | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	finishedCopy := make(chan struct{}) | 
					
						
							|  |  |  | 	if spec.Process != nil { | 
					
						
							|  |  |  | 		if spec.Process.Terminal { | 
					
						
							|  |  |  | 			copyConsole = true | 
					
						
							|  |  |  | 			// Create a listening socket for accepting the container's terminal's PTY master.
 | 
					
						
							|  |  |  | 			socketPath := filepath.Join(bundlePath, "console.sock") | 
					
						
							|  |  |  | 			consoleListener, err = net.ListenUnix("unix", &net.UnixAddr{Name: socketPath, Net: "unix"}) | 
					
						
							|  |  |  | 			if err != nil { | 
					
						
							|  |  |  | 				return 1, errors.Wrapf(err, "error creating socket to receive terminal descriptor") | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			// Add console socket arguments.
 | 
					
						
							| 
									
										
										
										
											2018-06-15 03:35:17 +08:00
										 |  |  | 			moreCreateArgs = append(moreCreateArgs, "--console-socket", socketPath) | 
					
						
							| 
									
										
										
										
											2018-03-21 23:19:47 +08:00
										 |  |  | 		} else { | 
					
						
							| 
									
										
										
										
											2018-06-15 04:41:40 +08:00
										 |  |  | 			copyPipes = true | 
					
						
							| 
									
										
										
										
											2018-03-17 05:19:29 +08:00
										 |  |  | 			// Figure out who should own the pipes.
 | 
					
						
							| 
									
										
										
										
											2018-06-07 23:25:47 +08:00
										 |  |  | 			uid, gid, err := util.GetHostRootIDs(spec) | 
					
						
							| 
									
										
										
										
											2018-03-17 05:19:29 +08:00
										 |  |  | 			if err != nil { | 
					
						
							|  |  |  | 				return 1, err | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2018-05-23 04:02:34 +08:00
										 |  |  | 			// Create stdio pipes.
 | 
					
						
							| 
									
										
										
										
											2018-03-17 05:19:29 +08:00
										 |  |  | 			if stdioPipe, err = runMakeStdioPipe(int(uid), int(gid)); err != nil { | 
					
						
							| 
									
										
										
										
											2018-05-23 04:02:34 +08:00
										 |  |  | 				return 1, err | 
					
						
							| 
									
										
										
										
											2018-03-21 23:19:47 +08:00
										 |  |  | 			} | 
					
						
							| 
									
										
										
										
											2018-06-01 01:34:32 +08:00
										 |  |  | 			errorFds = []int{stdioPipe[unix.Stdout][0], stdioPipe[unix.Stderr][0]} | 
					
						
							| 
									
										
										
										
											2018-03-21 23:19:47 +08:00
										 |  |  | 			// Set stdio to our pipes.
 | 
					
						
							| 
									
										
										
										
											2018-06-06 01:53:39 +08:00
										 |  |  | 			getCreateStdio = func() (io.ReadCloser, io.WriteCloser, io.WriteCloser) { | 
					
						
							| 
									
										
										
										
											2018-03-21 23:19:47 +08:00
										 |  |  | 				stdin := os.NewFile(uintptr(stdioPipe[unix.Stdin][0]), "/dev/stdin") | 
					
						
							|  |  |  | 				stdout := os.NewFile(uintptr(stdioPipe[unix.Stdout][1]), "/dev/stdout") | 
					
						
							|  |  |  | 				stderr := os.NewFile(uintptr(stdioPipe[unix.Stderr][1]), "/dev/stderr") | 
					
						
							|  |  |  | 				return stdin, stdout, stderr | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		if options.Quiet { | 
					
						
							|  |  |  | 			// Discard stdout.
 | 
					
						
							| 
									
										
										
										
											2018-06-06 01:53:39 +08:00
										 |  |  | 			getCreateStdio = func() (io.ReadCloser, io.WriteCloser, io.WriteCloser) { | 
					
						
							| 
									
										
										
										
											2018-03-21 23:19:47 +08:00
										 |  |  | 				return os.Stdin, nil, os.Stderr | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2018-01-24 00:17:31 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-03-21 23:19:47 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// Build the commands that we'll execute.
 | 
					
						
							|  |  |  | 	pidFile := filepath.Join(bundlePath, "pid") | 
					
						
							| 
									
										
										
										
											2018-06-15 03:35:17 +08:00
										 |  |  | 	args := append(append(append(options.Args, "create", "--bundle", bundlePath, "--pid-file", pidFile), moreCreateArgs...), containerName) | 
					
						
							| 
									
										
										
										
											2018-03-21 23:19:47 +08:00
										 |  |  | 	create := exec.Command(runtime, args...) | 
					
						
							|  |  |  | 	create.Dir = bundlePath | 
					
						
							|  |  |  | 	stdin, stdout, stderr := getCreateStdio() | 
					
						
							|  |  |  | 	create.Stdin, create.Stdout, create.Stderr = stdin, stdout, stderr | 
					
						
							|  |  |  | 	if create.SysProcAttr == nil { | 
					
						
							|  |  |  | 		create.SysProcAttr = &syscall.SysProcAttr{} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	args = append(options.Args, "start", containerName) | 
					
						
							|  |  |  | 	start := exec.Command(runtime, args...) | 
					
						
							|  |  |  | 	start.Dir = bundlePath | 
					
						
							|  |  |  | 	start.Stderr = os.Stderr | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	args = append(options.Args, "kill", containerName) | 
					
						
							|  |  |  | 	kill := exec.Command(runtime, args...) | 
					
						
							|  |  |  | 	kill.Dir = bundlePath | 
					
						
							|  |  |  | 	kill.Stderr = os.Stderr | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	args = append(options.Args, "delete", containerName) | 
					
						
							|  |  |  | 	del := exec.Command(runtime, args...) | 
					
						
							|  |  |  | 	del.Dir = bundlePath | 
					
						
							|  |  |  | 	del.Stderr = os.Stderr | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Actually create the container.
 | 
					
						
							|  |  |  | 	err = create.Run() | 
					
						
							| 
									
										
										
										
											2017-02-14 05:12:02 +08:00
										 |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2018-06-01 01:34:32 +08:00
										 |  |  | 		return 1, errors.Wrapf(err, "error creating container for %v: %s", spec.Process.Args, runCollectOutput(errorFds...)) | 
					
						
							| 
									
										
										
										
											2018-03-21 23:19:47 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	defer func() { | 
					
						
							|  |  |  | 		err2 := del.Run() | 
					
						
							|  |  |  | 		if err2 != nil { | 
					
						
							|  |  |  | 			if err == nil { | 
					
						
							|  |  |  | 				err = errors.Wrapf(err2, "error deleting container") | 
					
						
							|  |  |  | 			} else { | 
					
						
							|  |  |  | 				logrus.Infof("error deleting container: %v", err2) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	}() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Make sure we read the container's exit status when it exits.
 | 
					
						
							|  |  |  | 	pidValue, err := ioutil.ReadFile(pidFile) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return 1, errors.Wrapf(err, "error reading pid from %q", pidFile) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	pid, err := strconv.Atoi(strings.TrimSpace(string(pidValue))) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return 1, errors.Wrapf(err, "error parsing pid %s as a number", string(pidValue)) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	var reaping sync.WaitGroup | 
					
						
							|  |  |  | 	reaping.Add(1) | 
					
						
							|  |  |  | 	go func() { | 
					
						
							|  |  |  | 		defer reaping.Done() | 
					
						
							|  |  |  | 		var err error | 
					
						
							|  |  |  | 		_, err = unix.Wait4(pid, &wstatus, 0, nil) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			wstatus = 0 | 
					
						
							| 
									
										
											  
											
												Run: use a temporary mount namespace
Move the thread that launches the runtime helper into its own OS thread,
have it create its own mount namespace, and bind mount anything that we
want to eventually bind mount into the container, into a subdirectory of
the bundle directory, before running the helper.
When deciding what to bind mount in, make the volumes specified by the
user our highest priority, in case they've been specified in order to
override our default settings for a given location.  This required
breaking up setupMounts() to keep the complexity tests from complaining.
When we use a user namespace with the host IPC namespace, bind mount
/dev/shm and /dev/mqueue instead of mounting fresh copies.
If we're told to use a user namespace with the host PID namespace,
return an error, because that doesn't work.
When we use a user namespace with the host network namespace, bind mount
/sys instead of mounting a fresh one.
When we use the host UTS namespace, don't try to set a hostname.
Signed-off-by: Nalin Dahyabhai <nalin@redhat.com>
Closes: #700
Approved by: rhatdan
											
										 
											2018-05-03 00:31:05 +08:00
										 |  |  | 			logrus.Errorf("error waiting for container child process %d: %v\n", pid, err) | 
					
						
							| 
									
										
										
										
											2018-03-21 23:19:47 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	}() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-14 06:20:25 +08:00
										 |  |  | 	if configureNetwork { | 
					
						
							|  |  |  | 		teardown, err := runConfigureNetwork(options, configureNetwork, configureNetworks, pid, containerName, spec.Process.Args) | 
					
						
							|  |  |  | 		if teardown != nil { | 
					
						
							|  |  |  | 			defer teardown() | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return 1, err | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-06-15 04:41:40 +08:00
										 |  |  | 	if copyPipes { | 
					
						
							| 
									
										
										
										
											2018-03-21 23:19:47 +08:00
										 |  |  | 		// We don't need the ends of the pipes that belong to the container.
 | 
					
						
							|  |  |  | 		stdin.Close() | 
					
						
							|  |  |  | 		if stdout != nil { | 
					
						
							|  |  |  | 			stdout.Close() | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		stderr.Close() | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Handle stdio for the container in the background.
 | 
					
						
							|  |  |  | 	stdio.Add(1) | 
					
						
							| 
									
										
										
										
											2018-06-12 02:15:00 +08:00
										 |  |  | 	go runCopyStdio(&stdio, copyPipes, stdioPipe, copyConsole, consoleListener, finishCopy, finishedCopy, spec) | 
					
						
							| 
									
										
										
										
											2018-03-21 23:19:47 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// Start the container.
 | 
					
						
							|  |  |  | 	err = start.Run() | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return 1, errors.Wrapf(err, "error starting container") | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	stopped := false | 
					
						
							|  |  |  | 	defer func() { | 
					
						
							|  |  |  | 		if !stopped { | 
					
						
							|  |  |  | 			err2 := kill.Run() | 
					
						
							|  |  |  | 			if err2 != nil { | 
					
						
							|  |  |  | 				if err == nil { | 
					
						
							|  |  |  | 					err = errors.Wrapf(err2, "error stopping container") | 
					
						
							|  |  |  | 				} else { | 
					
						
							|  |  |  | 					logrus.Infof("error stopping container: %v", err2) | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	}() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Wait for the container to exit.
 | 
					
						
							|  |  |  | 	for { | 
					
						
							|  |  |  | 		now := time.Now() | 
					
						
							|  |  |  | 		var state specs.State | 
					
						
							|  |  |  | 		args = append(options.Args, "state", containerName) | 
					
						
							|  |  |  | 		stat := exec.Command(runtime, args...) | 
					
						
							|  |  |  | 		stat.Dir = bundlePath | 
					
						
							|  |  |  | 		stat.Stderr = os.Stderr | 
					
						
							|  |  |  | 		stateOutput, stateErr := stat.Output() | 
					
						
							|  |  |  | 		if stateErr != nil { | 
					
						
							|  |  |  | 			return 1, errors.Wrapf(stateErr, "error reading container state") | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if err = json.Unmarshal(stateOutput, &state); err != nil { | 
					
						
							|  |  |  | 			return 1, errors.Wrapf(stateErr, "error parsing container state %q", string(stateOutput)) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		switch state.Status { | 
					
						
							|  |  |  | 		case "running": | 
					
						
							|  |  |  | 		case "stopped": | 
					
						
							|  |  |  | 			stopped = true | 
					
						
							|  |  |  | 		default: | 
					
						
							|  |  |  | 			return 1, errors.Errorf("container status unexpectedly changed to %q", state.Status) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if stopped { | 
					
						
							|  |  |  | 			break | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		select { | 
					
						
							|  |  |  | 		case <-finishedCopy: | 
					
						
							|  |  |  | 			stopped = true | 
					
						
							|  |  |  | 		case <-time.After(time.Until(now.Add(100 * time.Millisecond))): | 
					
						
							|  |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if stopped { | 
					
						
							|  |  |  | 			break | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Close the writing end of the stop-handling-stdio notification pipe.
 | 
					
						
							|  |  |  | 	unix.Close(finishCopy[1]) | 
					
						
							|  |  |  | 	// Wait for the stdio copy goroutine to flush.
 | 
					
						
							|  |  |  | 	stdio.Wait() | 
					
						
							|  |  |  | 	// Wait until we finish reading the exit status.
 | 
					
						
							|  |  |  | 	reaping.Wait() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return wstatus, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
											  
											
												Run: use a temporary mount namespace
Move the thread that launches the runtime helper into its own OS thread,
have it create its own mount namespace, and bind mount anything that we
want to eventually bind mount into the container, into a subdirectory of
the bundle directory, before running the helper.
When deciding what to bind mount in, make the volumes specified by the
user our highest priority, in case they've been specified in order to
override our default settings for a given location.  This required
breaking up setupMounts() to keep the complexity tests from complaining.
When we use a user namespace with the host IPC namespace, bind mount
/dev/shm and /dev/mqueue instead of mounting fresh copies.
If we're told to use a user namespace with the host PID namespace,
return an error, because that doesn't work.
When we use a user namespace with the host network namespace, bind mount
/sys instead of mounting a fresh one.
When we use the host UTS namespace, don't try to set a hostname.
Signed-off-by: Nalin Dahyabhai <nalin@redhat.com>
Closes: #700
Approved by: rhatdan
											
										 
											2018-05-03 00:31:05 +08:00
										 |  |  | func runCollectOutput(fds ...int) string { | 
					
						
							|  |  |  | 	var b bytes.Buffer | 
					
						
							|  |  |  | 	buf := make([]byte, 8192) | 
					
						
							|  |  |  | 	for _, fd := range fds { | 
					
						
							|  |  |  | 		nread, err := unix.Read(fd, buf) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							| 
									
										
										
										
											2018-06-02 04:59:27 +08:00
										 |  |  | 			if errno, isErrno := err.(syscall.Errno); isErrno { | 
					
						
							|  |  |  | 				switch errno { | 
					
						
							|  |  |  | 				default: | 
					
						
							|  |  |  | 					logrus.Errorf("error reading from pipe %d: %v", fd, err) | 
					
						
							|  |  |  | 				case syscall.EINTR, syscall.EAGAIN: | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			} else { | 
					
						
							|  |  |  | 				logrus.Errorf("unable to wait for data from pipe %d: %v", fd, err) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			continue | 
					
						
							| 
									
										
											  
											
												Run: use a temporary mount namespace
Move the thread that launches the runtime helper into its own OS thread,
have it create its own mount namespace, and bind mount anything that we
want to eventually bind mount into the container, into a subdirectory of
the bundle directory, before running the helper.
When deciding what to bind mount in, make the volumes specified by the
user our highest priority, in case they've been specified in order to
override our default settings for a given location.  This required
breaking up setupMounts() to keep the complexity tests from complaining.
When we use a user namespace with the host IPC namespace, bind mount
/dev/shm and /dev/mqueue instead of mounting fresh copies.
If we're told to use a user namespace with the host PID namespace,
return an error, because that doesn't work.
When we use a user namespace with the host network namespace, bind mount
/sys instead of mounting a fresh one.
When we use the host UTS namespace, don't try to set a hostname.
Signed-off-by: Nalin Dahyabhai <nalin@redhat.com>
Closes: #700
Approved by: rhatdan
											
										 
											2018-05-03 00:31:05 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 		for nread > 0 { | 
					
						
							|  |  |  | 			r := buf[:nread] | 
					
						
							|  |  |  | 			if nwritten, err := b.Write(r); err != nil || nwritten != len(r) { | 
					
						
							|  |  |  | 				if nwritten != len(r) { | 
					
						
							|  |  |  | 					logrus.Errorf("error buffering data from pipe %d: %v", fd, err) | 
					
						
							|  |  |  | 					break | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			nread, err = unix.Read(fd, buf) | 
					
						
							|  |  |  | 			if err != nil { | 
					
						
							| 
									
										
										
										
											2018-06-02 04:59:27 +08:00
										 |  |  | 				if errno, isErrno := err.(syscall.Errno); isErrno { | 
					
						
							|  |  |  | 					switch errno { | 
					
						
							|  |  |  | 					default: | 
					
						
							|  |  |  | 						logrus.Errorf("error reading from pipe %d: %v", fd, err) | 
					
						
							|  |  |  | 					case syscall.EINTR, syscall.EAGAIN: | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 				} else { | 
					
						
							|  |  |  | 					logrus.Errorf("unable to wait for data from pipe %d: %v", fd, err) | 
					
						
							|  |  |  | 				} | 
					
						
							| 
									
										
											  
											
												Run: use a temporary mount namespace
Move the thread that launches the runtime helper into its own OS thread,
have it create its own mount namespace, and bind mount anything that we
want to eventually bind mount into the container, into a subdirectory of
the bundle directory, before running the helper.
When deciding what to bind mount in, make the volumes specified by the
user our highest priority, in case they've been specified in order to
override our default settings for a given location.  This required
breaking up setupMounts() to keep the complexity tests from complaining.
When we use a user namespace with the host IPC namespace, bind mount
/dev/shm and /dev/mqueue instead of mounting fresh copies.
If we're told to use a user namespace with the host PID namespace,
return an error, because that doesn't work.
When we use a user namespace with the host network namespace, bind mount
/sys instead of mounting a fresh one.
When we use the host UTS namespace, don't try to set a hostname.
Signed-off-by: Nalin Dahyabhai <nalin@redhat.com>
Closes: #700
Approved by: rhatdan
											
										 
											2018-05-03 00:31:05 +08:00
										 |  |  | 				break | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return b.String() | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-14 06:20:25 +08:00
										 |  |  | func runConfigureNetwork(options RunOptions, configureNetwork bool, configureNetworks []string, pid int, containerName string, command []string) (teardown func(), err error) { | 
					
						
							|  |  |  | 	var netconf, undo []*libcni.NetworkConfigList | 
					
						
							|  |  |  | 	// Scan for CNI configuration files.
 | 
					
						
							|  |  |  | 	confdir := options.CNIConfigDir | 
					
						
							|  |  |  | 	files, err := libcni.ConfFiles(confdir, []string{".conf"}) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, errors.Wrapf(err, "error finding CNI networking configuration files named *.conf in directory %q", confdir) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	lists, err := libcni.ConfFiles(confdir, []string{".conflist"}) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, errors.Wrapf(err, "error finding CNI networking configuration list files named *.conflist in directory %q", confdir) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	logrus.Debugf("CNI network configuration file list: %#v", append(files, lists...)) | 
					
						
							|  |  |  | 	// Read the CNI configuration files.
 | 
					
						
							|  |  |  | 	for _, file := range files { | 
					
						
							|  |  |  | 		nc, err := libcni.ConfFromFile(file) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return nil, errors.Wrapf(err, "error loading networking configuration from file %q for %v", file, command) | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2018-06-07 23:25:47 +08:00
										 |  |  | 		if len(configureNetworks) > 0 && nc.Network != nil && (nc.Network.Name == "" || !util.StringInSlice(nc.Network.Name, configureNetworks)) { | 
					
						
							| 
									
										
										
										
											2018-04-14 06:20:25 +08:00
										 |  |  | 			if nc.Network.Name == "" { | 
					
						
							|  |  |  | 				logrus.Debugf("configuration in %q has no name, skipping it", file) | 
					
						
							|  |  |  | 			} else { | 
					
						
							|  |  |  | 				logrus.Debugf("configuration in %q has name %q, skipping it", file, nc.Network.Name) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		cl, err := libcni.ConfListFromConf(nc) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return nil, errors.Wrapf(err, "error converting networking configuration from file %q for %v", file, command) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		logrus.Debugf("using network configuration from %q", file) | 
					
						
							|  |  |  | 		netconf = append(netconf, cl) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	for _, list := range lists { | 
					
						
							|  |  |  | 		cl, err := libcni.ConfListFromFile(list) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return nil, errors.Wrapf(err, "error loading networking configuration list from file %q for %v", list, command) | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2018-06-07 23:25:47 +08:00
										 |  |  | 		if len(configureNetworks) > 0 && (cl.Name == "" || !util.StringInSlice(cl.Name, configureNetworks)) { | 
					
						
							| 
									
										
										
										
											2018-04-14 06:20:25 +08:00
										 |  |  | 			if cl.Name == "" { | 
					
						
							|  |  |  | 				logrus.Debugf("configuration list in %q has no name, skipping it", list) | 
					
						
							|  |  |  | 			} else { | 
					
						
							|  |  |  | 				logrus.Debugf("configuration list in %q has name %q, skipping it", list, cl.Name) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		logrus.Debugf("using network configuration list from %q", list) | 
					
						
							|  |  |  | 		netconf = append(netconf, cl) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	// Make sure we can access the container's network namespace,
 | 
					
						
							|  |  |  | 	// even after it exits, to successfully tear down the
 | 
					
						
							|  |  |  | 	// interfaces.  Ensure this by opening a handle to the network
 | 
					
						
							|  |  |  | 	// namespace, and using our copy to both configure and
 | 
					
						
							|  |  |  | 	// deconfigure it.
 | 
					
						
							|  |  |  | 	netns := fmt.Sprintf("/proc/%d/ns/net", pid) | 
					
						
							|  |  |  | 	netFD, err := unix.Open(netns, unix.O_RDONLY, 0) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, errors.Wrapf(err, "error opening network namespace for %v", command) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	mynetns := fmt.Sprintf("/proc/%d/fd/%d", unix.Getpid(), netFD) | 
					
						
							|  |  |  | 	// Build our search path for the plugins.
 | 
					
						
							|  |  |  | 	pluginPaths := strings.Split(options.CNIPluginPath, string(os.PathListSeparator)) | 
					
						
							|  |  |  | 	cni := libcni.CNIConfig{Path: pluginPaths} | 
					
						
							|  |  |  | 	// Configure the interfaces.
 | 
					
						
							|  |  |  | 	rtconf := make(map[*libcni.NetworkConfigList]*libcni.RuntimeConf) | 
					
						
							|  |  |  | 	teardown = func() { | 
					
						
							|  |  |  | 		for _, nc := range undo { | 
					
						
							|  |  |  | 			if err = cni.DelNetworkList(nc, rtconf[nc]); err != nil { | 
					
						
							|  |  |  | 				logrus.Errorf("error cleaning up network %v for %v: %v", rtconf[nc].IfName, command, err) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		unix.Close(netFD) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	for i, nc := range netconf { | 
					
						
							|  |  |  | 		// Build the runtime config for use with this network configuration.
 | 
					
						
							|  |  |  | 		rtconf[nc] = &libcni.RuntimeConf{ | 
					
						
							|  |  |  | 			ContainerID:    containerName, | 
					
						
							|  |  |  | 			NetNS:          mynetns, | 
					
						
							|  |  |  | 			IfName:         fmt.Sprintf("if%d", i), | 
					
						
							|  |  |  | 			Args:           [][2]string{}, | 
					
						
							|  |  |  | 			CapabilityArgs: map[string]interface{}{}, | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		// Bring it up.
 | 
					
						
							|  |  |  | 		_, err := cni.AddNetworkList(nc, rtconf[nc]) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return teardown, errors.Wrapf(err, "error configuring network list %v for %v", rtconf[nc].IfName, command) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		// Add it to the list of networks to take down when the container process exits.
 | 
					
						
							|  |  |  | 		undo = append([]*libcni.NetworkConfigList{nc}, undo...) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return teardown, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-06-12 02:15:00 +08:00
										 |  |  | func runCopyStdio(stdio *sync.WaitGroup, copyPipes bool, stdioPipe [][]int, copyConsole bool, consoleListener *net.UnixListener, finishCopy []int, finishedCopy chan struct{}, spec *specs.Spec) { | 
					
						
							| 
									
										
										
										
											2018-03-21 23:19:47 +08:00
										 |  |  | 	defer func() { | 
					
						
							|  |  |  | 		unix.Close(finishCopy[0]) | 
					
						
							| 
									
										
										
										
											2018-06-15 04:41:40 +08:00
										 |  |  | 		if copyPipes { | 
					
						
							| 
									
										
										
										
											2018-03-21 23:19:47 +08:00
										 |  |  | 			unix.Close(stdioPipe[unix.Stdin][1]) | 
					
						
							|  |  |  | 			unix.Close(stdioPipe[unix.Stdout][0]) | 
					
						
							|  |  |  | 			unix.Close(stdioPipe[unix.Stderr][0]) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		stdio.Done() | 
					
						
							|  |  |  | 		finishedCopy <- struct{}{} | 
					
						
							|  |  |  | 	}() | 
					
						
							| 
									
										
										
										
											2018-06-15 06:04:02 +08:00
										 |  |  | 	// Map describing where data on an incoming descriptor should go.
 | 
					
						
							|  |  |  | 	relayMap := make(map[int]int) | 
					
						
							|  |  |  | 	// Map describing incoming and outgoing descriptors.
 | 
					
						
							|  |  |  | 	readDesc := make(map[int]string) | 
					
						
							|  |  |  | 	writeDesc := make(map[int]string) | 
					
						
							|  |  |  | 	// Buffers.
 | 
					
						
							|  |  |  | 	relayBuffer := make(map[int]*bytes.Buffer) | 
					
						
							|  |  |  | 	// Set up the terminal descriptor or pipes for polling.
 | 
					
						
							| 
									
										
										
										
											2018-03-21 23:19:47 +08:00
										 |  |  | 	if copyConsole { | 
					
						
							|  |  |  | 		// Accept a connection over our listening socket.
 | 
					
						
							| 
									
										
										
										
											2018-06-12 02:15:00 +08:00
										 |  |  | 		fd, err := runAcceptTerminal(consoleListener, spec.Process.ConsoleSize) | 
					
						
							| 
									
										
										
										
											2018-03-21 23:19:47 +08:00
										 |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			logrus.Errorf("%v", err) | 
					
						
							|  |  |  | 			return | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2018-06-15 06:04:02 +08:00
										 |  |  | 		terminalFD := fd | 
					
						
							|  |  |  | 		// Input from our stdin, output from the terminal descriptor.
 | 
					
						
							|  |  |  | 		relayMap[unix.Stdin] = terminalFD | 
					
						
							|  |  |  | 		readDesc[unix.Stdin] = "stdin" | 
					
						
							|  |  |  | 		relayBuffer[terminalFD] = new(bytes.Buffer) | 
					
						
							|  |  |  | 		writeDesc[terminalFD] = "container terminal input" | 
					
						
							|  |  |  | 		relayMap[terminalFD] = unix.Stdout | 
					
						
							|  |  |  | 		readDesc[terminalFD] = "container terminal output" | 
					
						
							|  |  |  | 		relayBuffer[unix.Stdout] = new(bytes.Buffer) | 
					
						
							|  |  |  | 		writeDesc[unix.Stdout] = "output" | 
					
						
							| 
									
										
										
										
											2018-03-21 23:19:47 +08:00
										 |  |  | 		// Set our terminal's mode to raw, to pass handling of special
 | 
					
						
							|  |  |  | 		// terminal input to the terminal in the container.
 | 
					
						
							| 
									
										
										
										
											2018-06-12 02:15:00 +08:00
										 |  |  | 		if terminal.IsTerminal(unix.Stdin) { | 
					
						
							|  |  |  | 			if state, err := terminal.MakeRaw(unix.Stdin); err != nil { | 
					
						
							|  |  |  | 				logrus.Warnf("error setting terminal state: %v", err) | 
					
						
							|  |  |  | 			} else { | 
					
						
							|  |  |  | 				defer func() { | 
					
						
							|  |  |  | 					if err = terminal.Restore(unix.Stdin, state); err != nil { | 
					
						
							|  |  |  | 						logrus.Errorf("unable to restore terminal state: %v", err) | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 				}() | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2018-03-21 23:19:47 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-06-15 04:41:40 +08:00
										 |  |  | 	if copyPipes { | 
					
						
							| 
									
										
										
										
											2018-03-21 23:19:47 +08:00
										 |  |  | 		// Input from our stdin, output from the stdout and stderr pipes.
 | 
					
						
							|  |  |  | 		relayMap[unix.Stdin] = stdioPipe[unix.Stdin][1] | 
					
						
							| 
									
										
										
										
											2018-06-13 21:56:28 +08:00
										 |  |  | 		readDesc[unix.Stdin] = "stdin" | 
					
						
							|  |  |  | 		relayBuffer[stdioPipe[unix.Stdin][1]] = new(bytes.Buffer) | 
					
						
							|  |  |  | 		writeDesc[stdioPipe[unix.Stdin][1]] = "container stdin" | 
					
						
							| 
									
										
										
										
											2018-03-21 23:19:47 +08:00
										 |  |  | 		relayMap[stdioPipe[unix.Stdout][0]] = unix.Stdout | 
					
						
							| 
									
										
										
										
											2018-06-13 21:56:28 +08:00
										 |  |  | 		readDesc[stdioPipe[unix.Stdout][0]] = "container stdout" | 
					
						
							|  |  |  | 		relayBuffer[unix.Stdout] = new(bytes.Buffer) | 
					
						
							|  |  |  | 		writeDesc[unix.Stdout] = "stdout" | 
					
						
							| 
									
										
										
										
											2018-03-21 23:19:47 +08:00
										 |  |  | 		relayMap[stdioPipe[unix.Stderr][0]] = unix.Stderr | 
					
						
							| 
									
										
										
										
											2018-06-13 21:56:28 +08:00
										 |  |  | 		readDesc[stdioPipe[unix.Stderr][0]] = "container stderr" | 
					
						
							|  |  |  | 		relayBuffer[unix.Stderr] = new(bytes.Buffer) | 
					
						
							|  |  |  | 		writeDesc[unix.Stderr] = "stderr" | 
					
						
							| 
									
										
										
										
											2018-03-21 23:19:47 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	// Set our reading descriptors to non-blocking.
 | 
					
						
							|  |  |  | 	for fd := range relayMap { | 
					
						
							|  |  |  | 		if err := unix.SetNonblock(fd, true); err != nil { | 
					
						
							| 
									
										
										
										
											2018-06-13 21:56:28 +08:00
										 |  |  | 			logrus.Errorf("error setting %s to nonblocking: %v", readDesc[fd], err) | 
					
						
							| 
									
										
										
										
											2018-03-21 23:19:47 +08:00
										 |  |  | 			return | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-06-13 21:56:28 +08:00
										 |  |  | 	// A helper that returns false if err is an error that would cause us
 | 
					
						
							|  |  |  | 	// to give up.
 | 
					
						
							|  |  |  | 	logIfNotRetryable := func(err error, what string) (retry bool) { | 
					
						
							|  |  |  | 		if err == nil { | 
					
						
							|  |  |  | 			return true | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if errno, isErrno := err.(syscall.Errno); isErrno { | 
					
						
							|  |  |  | 			switch errno { | 
					
						
							|  |  |  | 			case syscall.EINTR, syscall.EAGAIN: | 
					
						
							|  |  |  | 				return true | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		logrus.Error(what) | 
					
						
							|  |  |  | 		return false | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-03-21 23:19:47 +08:00
										 |  |  | 	// Pass data back and forth.
 | 
					
						
							| 
									
										
										
										
											2018-06-13 21:56:28 +08:00
										 |  |  | 	pollTimeout := -1 | 
					
						
							| 
									
										
										
										
											2018-06-15 06:04:02 +08:00
										 |  |  | 	for len(relayMap) > 0 { | 
					
						
							| 
									
										
										
										
											2018-03-21 23:19:47 +08:00
										 |  |  | 		// Start building the list of descriptors to poll.
 | 
					
						
							| 
									
										
										
										
											2018-06-15 06:04:02 +08:00
										 |  |  | 		pollFds := make([]unix.PollFd, 0, len(relayMap)+1) | 
					
						
							| 
									
										
										
										
											2018-03-21 23:19:47 +08:00
										 |  |  | 		// Poll for a notification that we should stop handling stdio.
 | 
					
						
							|  |  |  | 		pollFds = append(pollFds, unix.PollFd{Fd: int32(finishCopy[0]), Events: unix.POLLIN | unix.POLLHUP}) | 
					
						
							|  |  |  | 		// Poll on our reading descriptors.
 | 
					
						
							|  |  |  | 		for rfd := range relayMap { | 
					
						
							|  |  |  | 			pollFds = append(pollFds, unix.PollFd{Fd: int32(rfd), Events: unix.POLLIN | unix.POLLHUP}) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		buf := make([]byte, 8192) | 
					
						
							|  |  |  | 		// Wait for new data from any input descriptor, or a notification that we're done.
 | 
					
						
							| 
									
										
										
										
											2018-06-13 21:56:28 +08:00
										 |  |  | 		_, err := unix.Poll(pollFds, pollTimeout) | 
					
						
							|  |  |  | 		if !logIfNotRetryable(err, fmt.Sprintf("error waiting for stdio/terminal data to relay: %v", err)) { | 
					
						
							| 
									
										
										
										
											2018-03-21 23:19:47 +08:00
										 |  |  | 			return | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2018-06-15 01:43:54 +08:00
										 |  |  | 		removes := make(map[int]struct{}) | 
					
						
							| 
									
										
										
										
											2018-03-21 23:19:47 +08:00
										 |  |  | 		for _, pollFd := range pollFds { | 
					
						
							|  |  |  | 			// If this descriptor's just been closed from the other end, mark it for
 | 
					
						
							|  |  |  | 			// removal from the set that we're checking for.
 | 
					
						
							|  |  |  | 			if pollFd.Revents&unix.POLLHUP == unix.POLLHUP { | 
					
						
							| 
									
										
										
										
											2018-06-15 01:43:54 +08:00
										 |  |  | 				removes[int(pollFd.Fd)] = struct{}{} | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			// If the descriptor was closed elsewhere, remove it from our list.
 | 
					
						
							|  |  |  | 			if pollFd.Revents&unix.POLLNVAL != 0 { | 
					
						
							|  |  |  | 				logrus.Debugf("error polling descriptor %d: closed?", pollFd.Fd) | 
					
						
							|  |  |  | 				removes[int(pollFd.Fd)] = struct{}{} | 
					
						
							| 
									
										
										
										
											2018-03-21 23:19:47 +08:00
										 |  |  | 			} | 
					
						
							| 
									
										
										
										
											2018-06-06 22:05:17 +08:00
										 |  |  | 			// If the POLLIN flag isn't set, then there's no data to be read from this descriptor.
 | 
					
						
							| 
									
										
										
										
											2018-03-21 23:19:47 +08:00
										 |  |  | 			if pollFd.Revents&unix.POLLIN == 0 { | 
					
						
							| 
									
										
										
										
											2018-06-06 22:05:17 +08:00
										 |  |  | 				// If we're using pipes and it's our stdin and it's closed, close the writing
 | 
					
						
							|  |  |  | 				// end of the corresponding pipe.
 | 
					
						
							| 
									
										
										
										
											2018-06-15 04:41:40 +08:00
										 |  |  | 				if copyPipes && int(pollFd.Fd) == unix.Stdin && pollFd.Revents&unix.POLLHUP != 0 { | 
					
						
							| 
									
										
										
										
											2018-03-21 23:19:47 +08:00
										 |  |  | 					unix.Close(stdioPipe[unix.Stdin][1]) | 
					
						
							|  |  |  | 					stdioPipe[unix.Stdin][1] = -1 | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				continue | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2018-06-13 21:56:28 +08:00
										 |  |  | 			// Read whatever there is to be read.
 | 
					
						
							| 
									
										
										
										
											2018-03-21 23:19:47 +08:00
										 |  |  | 			readFD := int(pollFd.Fd) | 
					
						
							|  |  |  | 			writeFD, needToRelay := relayMap[readFD] | 
					
						
							|  |  |  | 			if needToRelay { | 
					
						
							|  |  |  | 				n, err := unix.Read(readFD, buf) | 
					
						
							| 
									
										
										
										
											2018-06-13 21:56:28 +08:00
										 |  |  | 				if !logIfNotRetryable(err, fmt.Sprintf("unable to read %s data: %v", readDesc[readFD], err)) { | 
					
						
							|  |  |  | 					return | 
					
						
							| 
									
										
										
										
											2018-03-21 23:19:47 +08:00
										 |  |  | 				} | 
					
						
							|  |  |  | 				// If it's zero-length on our stdin and we're
 | 
					
						
							|  |  |  | 				// using pipes, it's an EOF, so close the stdin
 | 
					
						
							|  |  |  | 				// pipe's writing end.
 | 
					
						
							| 
									
										
										
										
											2018-06-15 04:41:40 +08:00
										 |  |  | 				if n == 0 && copyPipes && int(pollFd.Fd) == unix.Stdin { | 
					
						
							| 
									
										
										
										
											2018-03-21 23:19:47 +08:00
										 |  |  | 					unix.Close(stdioPipe[unix.Stdin][1]) | 
					
						
							|  |  |  | 					stdioPipe[unix.Stdin][1] = -1 | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				if n > 0 { | 
					
						
							| 
									
										
										
										
											2018-06-13 21:56:28 +08:00
										 |  |  | 					// Buffer the data in case we get blocked on where they need to go.
 | 
					
						
							|  |  |  | 					relayBuffer[writeFD].Write(buf[:n]) | 
					
						
							| 
									
										
										
										
											2018-03-21 23:19:47 +08:00
										 |  |  | 				} | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2018-06-13 21:56:28 +08:00
										 |  |  | 		// Try to drain the output buffers.  Set the default timeout
 | 
					
						
							|  |  |  | 		// for the next poll() to 100ms if we still have data to write.
 | 
					
						
							|  |  |  | 		pollTimeout = -1 | 
					
						
							|  |  |  | 		for writeFD := range relayBuffer { | 
					
						
							|  |  |  | 			if relayBuffer[writeFD].Len() > 0 { | 
					
						
							|  |  |  | 				n, err := unix.Write(writeFD, relayBuffer[writeFD].Bytes()) | 
					
						
							|  |  |  | 				if !logIfNotRetryable(err, fmt.Sprintf("unable to write %s data: %v", writeDesc[writeFD], err)) { | 
					
						
							|  |  |  | 					return | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				if n > 0 { | 
					
						
							|  |  |  | 					relayBuffer[writeFD].Next(n) | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			if relayBuffer[writeFD].Len() > 0 { | 
					
						
							|  |  |  | 				pollTimeout = 100 | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2018-03-21 23:19:47 +08:00
										 |  |  | 		// Remove any descriptors which we don't need to poll any more from the poll descriptor list.
 | 
					
						
							| 
									
										
										
										
											2018-06-15 01:43:54 +08:00
										 |  |  | 		for remove := range removes { | 
					
						
							| 
									
										
										
										
											2018-03-21 23:19:47 +08:00
										 |  |  | 			delete(relayMap, remove) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		// If the we-can-return pipe had anything for us, we're done.
 | 
					
						
							|  |  |  | 		for _, pollFd := range pollFds { | 
					
						
							|  |  |  | 			if int(pollFd.Fd) == finishCopy[0] && pollFd.Revents != 0 { | 
					
						
							|  |  |  | 				// The pipe is closed, indicating that we can stop now.
 | 
					
						
							|  |  |  | 				return | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-06-12 02:15:00 +08:00
										 |  |  | func runAcceptTerminal(consoleListener *net.UnixListener, terminalSize *specs.Box) (int, error) { | 
					
						
							| 
									
										
										
										
											2018-03-21 23:19:47 +08:00
										 |  |  | 	defer consoleListener.Close() | 
					
						
							|  |  |  | 	c, err := consoleListener.AcceptUnix() | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return -1, errors.Wrapf(err, "error accepting socket descriptor connection") | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	defer c.Close() | 
					
						
							|  |  |  | 	// Expect a control message over our new connection.
 | 
					
						
							|  |  |  | 	b := make([]byte, 8192) | 
					
						
							|  |  |  | 	oob := make([]byte, 8192) | 
					
						
							|  |  |  | 	n, oobn, _, _, err := c.ReadMsgUnix(b, oob) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return -1, errors.Wrapf(err, "error reading socket descriptor: %v") | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if n > 0 { | 
					
						
							|  |  |  | 		logrus.Debugf("socket descriptor is for %q", string(b[:n])) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if oobn > len(oob) { | 
					
						
							|  |  |  | 		return -1, errors.Errorf("too much out-of-bounds data (%d bytes)", oobn) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	// Parse the control message.
 | 
					
						
							|  |  |  | 	scm, err := unix.ParseSocketControlMessage(oob[:oobn]) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return -1, errors.Wrapf(err, "error parsing out-of-bound data as a socket control message") | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	logrus.Debugf("control messages: %v", scm) | 
					
						
							|  |  |  | 	// Expect to get a descriptor.
 | 
					
						
							|  |  |  | 	terminalFD := -1 | 
					
						
							|  |  |  | 	for i := range scm { | 
					
						
							|  |  |  | 		fds, err := unix.ParseUnixRights(&scm[i]) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return -1, errors.Wrapf(err, "error parsing unix rights control message: %v") | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		logrus.Debugf("fds: %v", fds) | 
					
						
							|  |  |  | 		if len(fds) == 0 { | 
					
						
							|  |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		terminalFD = fds[0] | 
					
						
							|  |  |  | 		break | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if terminalFD == -1 { | 
					
						
							|  |  |  | 		return -1, errors.Errorf("unable to read terminal descriptor") | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-06-12 02:15:00 +08:00
										 |  |  | 	// Set the pseudoterminal's size to the configured size, or our own.
 | 
					
						
							|  |  |  | 	winsize := &unix.Winsize{} | 
					
						
							|  |  |  | 	if terminalSize != nil { | 
					
						
							|  |  |  | 		// Use configured sizes.
 | 
					
						
							|  |  |  | 		winsize.Row = uint16(terminalSize.Height) | 
					
						
							|  |  |  | 		winsize.Col = uint16(terminalSize.Width) | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		if terminal.IsTerminal(unix.Stdin) { | 
					
						
							|  |  |  | 			// Use the size of our terminal.
 | 
					
						
							|  |  |  | 			if winsize, err = unix.IoctlGetWinsize(unix.Stdin, unix.TIOCGWINSZ); err != nil { | 
					
						
							|  |  |  | 				logrus.Warnf("error reading size of controlling terminal: %v", err) | 
					
						
							|  |  |  | 				winsize.Row = 0 | 
					
						
							|  |  |  | 				winsize.Col = 0 | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2018-03-21 23:19:47 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-06-12 02:15:00 +08:00
										 |  |  | 	if winsize.Row != 0 && winsize.Col != 0 { | 
					
						
							|  |  |  | 		if err = unix.IoctlSetWinsize(terminalFD, unix.TIOCSWINSZ, winsize); err != nil { | 
					
						
							|  |  |  | 			logrus.Warnf("error setting size of container pseudoterminal: %v", err) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		// FIXME - if we're connected to a terminal, we should
 | 
					
						
							|  |  |  | 		// be passing the updated terminal size down when we
 | 
					
						
							|  |  |  | 		// receive a SIGWINCH.
 | 
					
						
							| 
									
										
										
										
											2018-03-21 23:19:47 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	return terminalFD, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-05-23 04:02:34 +08:00
										 |  |  | // Create pipes to use for relaying stdio.
 | 
					
						
							| 
									
										
										
										
											2018-03-17 05:19:29 +08:00
										 |  |  | func runMakeStdioPipe(uid, gid int) ([][]int, error) { | 
					
						
							| 
									
										
										
										
											2018-05-23 04:02:34 +08:00
										 |  |  | 	stdioPipe := make([][]int, 3) | 
					
						
							|  |  |  | 	for i := range stdioPipe { | 
					
						
							|  |  |  | 		stdioPipe[i] = make([]int, 2) | 
					
						
							|  |  |  | 		if err := unix.Pipe(stdioPipe[i]); err != nil { | 
					
						
							|  |  |  | 			return nil, errors.Wrapf(err, "error creating pipe for container FD %d", i) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-03-17 05:19:29 +08:00
										 |  |  | 	if err := unix.Fchown(stdioPipe[unix.Stdin][0], uid, gid); err != nil { | 
					
						
							|  |  |  | 		return nil, errors.Wrapf(err, "error setting owner of stdin pipe descriptor") | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if err := unix.Fchown(stdioPipe[unix.Stdout][1], uid, gid); err != nil { | 
					
						
							|  |  |  | 		return nil, errors.Wrapf(err, "error setting owner of stdout pipe descriptor") | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if err := unix.Fchown(stdioPipe[unix.Stderr][1], uid, gid); err != nil { | 
					
						
							|  |  |  | 		return nil, errors.Wrapf(err, "error setting owner of stderr pipe descriptor") | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-05-23 04:02:34 +08:00
										 |  |  | 	return stdioPipe, nil | 
					
						
							|  |  |  | } |