| 
									
										
										
										
											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" | 
					
						
							|  |  |  | 	"sort" | 
					
						
							| 
									
										
										
										
											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" | 
					
						
							| 
									
										
											  
											
												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
										 |  |  | 	"github.com/containers/storage/pkg/idtools" | 
					
						
							| 
									
										
										
										
											2017-02-14 05:12:02 +08:00
										 |  |  | 	"github.com/containers/storage/pkg/ioutils" | 
					
						
							| 
									
										
											  
											
												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
										 |  |  | 	"github.com/containers/storage/pkg/mount" | 
					
						
							| 
									
										
										
										
											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-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 = "/" | 
					
						
							| 
									
										
										
										
											2017-02-18 02:54:49 +08:00
										 |  |  | 	// DefaultRuntime is the default command to use to run the container.
 | 
					
						
							|  |  |  | 	DefaultRuntime = "runc" | 
					
						
							| 
									
										
										
										
											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-01-24 00:17:31 +08:00
										 |  |  | 	// Quiet tells the run to turn off output to stdout.
 | 
					
						
							|  |  |  | 	Quiet bool | 
					
						
							| 
									
										
										
										
											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 { | 
					
						
							|  |  |  | 		fmt.Fprintln(buf, host) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	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 | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
											  
											
												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 (b *Builder) setupMounts(mountPoint string, spec *specs.Spec, optionMounts []specs.Mount, bindFiles map[string]string, builtinVolumes, volumeMounts []string, shmSize string, namespaceOptions NamespaceOptions) error { | 
					
						
							|  |  |  | 	// 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", | 
					
						
							|  |  |  | 					Options:     []string{"nobuildahbind", "rbind", "nosuid", "noexec", "nodev"}, | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											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", | 
					
						
							|  |  |  | 					Options:     []string{"nobuildahbind", "rbind", "nosuid", "noexec", "nodev"}, | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											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", | 
					
						
							|  |  |  | 					Options:     []string{"nobuildahbind", "rbind", "nosuid", "noexec", "nodev", "ro"}, | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											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", | 
					
						
							|  |  |  | 			Options:     []string{"nobuildahbind", "nosuid", "noexec", "nodev", "relatime", "ro"}, | 
					
						
							|  |  |  | 		}} | 
					
						
							| 
									
										
										
										
											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.
 | 
					
						
							|  |  |  | 	bindFileMounts, err := runSetupBoundFiles(bindFiles) | 
					
						
							|  |  |  | 	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.
 | 
					
						
							|  |  |  | 	rootUID, rootGID, err := getHostRootIDs(spec) | 
					
						
							|  |  |  | 	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.
 | 
					
						
							|  |  |  | 	copyWithTar := b.copyWithTar(nil) | 
					
						
							|  |  |  | 	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 | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func runSetupBoundFiles(bindFiles map[string]string) (mounts []specs.Mount, err error) { | 
					
						
							|  |  |  | 	for dest, src := range bindFiles { | 
					
						
							|  |  |  | 		mounts = append(mounts, specs.Mount{ | 
					
						
							|  |  |  | 			Source:      src, | 
					
						
							|  |  |  | 			Destination: dest, | 
					
						
							|  |  |  | 			Type:        "bind", | 
					
						
							|  |  |  | 			Options:     []string{"rbind"}, | 
					
						
							|  |  |  | 		}) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	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-03-13 01:53:12 +08:00
										 |  |  | 	copyFileWithTar := b.copyFileWithTar(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) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func setupSeccomp(spec *specs.Spec, seccompProfilePath string) error { | 
					
						
							|  |  |  | 	if seccompProfilePath != "unconfined" { | 
					
						
							|  |  |  | 		if seccompProfilePath != "" { | 
					
						
							|  |  |  | 			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) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			spec.Linux.Seccomp = seccompConfig | 
					
						
							|  |  |  | 		} else { | 
					
						
							|  |  |  | 			seccompConfig, err := seccomp.GetDefaultProfile(spec) | 
					
						
							|  |  |  | 			if err != nil { | 
					
						
							|  |  |  | 				return errors.Wrapf(err, "loading seccomp profile (%s) failed", seccompProfilePath) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			spec.Linux.Seccomp = seccompConfig | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func setupApparmor(spec *specs.Spec, apparmorProfile string) error { | 
					
						
							|  |  |  | 	spec.Process.ApparmorProfile = apparmorProfile | 
					
						
							|  |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func setupTerminal(g *generate.Generator, terminalPolicy TerminalPolicy) { | 
					
						
							|  |  |  | 	switch terminalPolicy { | 
					
						
							|  |  |  | 	case DefaultTerminal: | 
					
						
							|  |  |  | 		g.SetProcessTerminal(terminal.IsTerminal(int(os.Stdout.Fd()))) | 
					
						
							|  |  |  | 	case WithTerminal: | 
					
						
							|  |  |  | 		g.SetProcessTerminal(true) | 
					
						
							|  |  |  | 	case WithoutTerminal: | 
					
						
							|  |  |  | 		g.SetProcessTerminal(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
										 |  |  | 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
										 |  |  | 		} | 
					
						
							|  |  |  | 		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 { | 
					
						
							|  |  |  | 			mappings, err := getProcIDMappings("/proc/self/uid_map") | 
					
						
							|  |  |  | 			if 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, err | 
					
						
							| 
									
										
										
										
											2018-04-24 03:55:46 +08:00
										 |  |  | 			} | 
					
						
							|  |  |  | 			for _, m := range mappings { | 
					
						
							|  |  |  | 				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 { | 
					
						
							|  |  |  | 			mappings, err := getProcIDMappings("/proc/self/gid_map") | 
					
						
							|  |  |  | 			if 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, err | 
					
						
							| 
									
										
										
										
											2018-04-24 03:55:46 +08:00
										 |  |  | 			} | 
					
						
							|  |  |  | 			for _, m := range mappings { | 
					
						
							|  |  |  | 				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 03:09:40 +08:00
										 |  |  | 	p, err := ioutil.TempDir(os.TempDir(), Package) | 
					
						
							|  |  |  | 	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
										 |  |  | 
 | 
					
						
							|  |  |  | 	setupTerminal(g, options.Terminal) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	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-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 | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	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-03-08 07:11:43 +08:00
										 |  |  | 	g = nil | 
					
						
							| 
									
										
										
										
											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-03-07 07:13:24 +08:00
										 |  |  | 	// Set the seccomp configuration using the specified profile name.
 | 
					
						
							| 
									
										
										
										
											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, | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
											  
											
												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
										 |  |  | 	err = b.setupMounts(mountPoint, spec, 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 | 
					
						
							|  |  |  | 	cmd.Stdin = os.Stdin | 
					
						
							|  |  |  | 	cmd.Stdout = os.Stdout | 
					
						
							|  |  |  | 	cmd.Stderr = os.Stderr | 
					
						
							|  |  |  | 	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) { | 
					
						
							| 
									
										
											  
											
												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 up bind mounts for things that a namespaced user might not be able to get to directly.
 | 
					
						
							|  |  |  | 	runtime.LockOSThread() | 
					
						
							|  |  |  | 	unmountAll, err := runSetupIntermediateMountNamespace(spec, bundlePath) | 
					
						
							|  |  |  | 	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 == "" { | 
					
						
							|  |  |  | 		runtime = DefaultRuntime | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-03-21 23:19:47 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-21 23:19:47 +08:00
										 |  |  | 	// Default to not specifying a console socket location.
 | 
					
						
							|  |  |  | 	moreCreateArgs := func() []string { return nil } | 
					
						
							|  |  |  | 	// Default to just passing down our stdio.
 | 
					
						
							|  |  |  | 	getCreateStdio := func() (*os.File, *os.File, *os.File) { return os.Stdin, os.Stdout, os.Stderr } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// 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 | 
					
						
							|  |  |  | 	copyStdio := false | 
					
						
							|  |  |  | 	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.
 | 
					
						
							|  |  |  | 			moreCreateArgs = func() []string { return []string{"--console-socket", socketPath} } | 
					
						
							|  |  |  | 		} else { | 
					
						
							|  |  |  | 			copyStdio = true | 
					
						
							| 
									
										
										
										
											2018-03-17 05:19:29 +08:00
										 |  |  | 			// Figure out who should own the pipes.
 | 
					
						
							|  |  |  | 			uid, gid, err := getHostRootIDs(spec) | 
					
						
							|  |  |  | 			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.
 | 
					
						
							|  |  |  | 			getCreateStdio = func() (*os.File, *os.File, *os.File) { | 
					
						
							|  |  |  | 				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.
 | 
					
						
							|  |  |  | 			getCreateStdio = func() (*os.File, *os.File, *os.File) { | 
					
						
							|  |  |  | 				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") | 
					
						
							|  |  |  | 	args := append(append(append(options.Args, "create", "--bundle", bundlePath, "--pid-file", pidFile), moreCreateArgs()...), containerName) | 
					
						
							|  |  |  | 	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{} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	runSetDeathSig(create) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	args = append(options.Args, "start", containerName) | 
					
						
							|  |  |  | 	start := exec.Command(runtime, args...) | 
					
						
							|  |  |  | 	start.Dir = bundlePath | 
					
						
							|  |  |  | 	start.Stderr = os.Stderr | 
					
						
							|  |  |  | 	runSetDeathSig(start) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	args = append(options.Args, "kill", containerName) | 
					
						
							|  |  |  | 	kill := exec.Command(runtime, args...) | 
					
						
							|  |  |  | 	kill.Dir = bundlePath | 
					
						
							|  |  |  | 	kill.Stderr = os.Stderr | 
					
						
							|  |  |  | 	runSetDeathSig(kill) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	args = append(options.Args, "delete", containerName) | 
					
						
							|  |  |  | 	del := exec.Command(runtime, args...) | 
					
						
							|  |  |  | 	del.Dir = bundlePath | 
					
						
							|  |  |  | 	del.Stderr = os.Stderr | 
					
						
							|  |  |  | 	runSetDeathSig(del) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// 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-03-21 23:19:47 +08:00
										 |  |  | 	if copyStdio { | 
					
						
							|  |  |  | 		// 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) | 
					
						
							|  |  |  | 	go runCopyStdio(&stdio, copyStdio, stdioPipe, copyConsole, consoleListener, finishCopy, finishedCopy) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// 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 { | 
					
						
							|  |  |  | 		if err := unix.SetNonblock(fd, true); err != nil { | 
					
						
							|  |  |  | 			logrus.Errorf("error setting pipe descriptor %d nonblocking: %v", fd, err) | 
					
						
							|  |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		nread, err := unix.Read(fd, buf) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			logrus.Errorf("error reading from pipe %d: %v", fd, err) | 
					
						
							|  |  |  | 			break | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		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 { | 
					
						
							|  |  |  | 				logrus.Errorf("error reading from pipe %d: %v", fd, err) | 
					
						
							|  |  |  | 				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) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if len(configureNetworks) > 0 && nc.Network != nil && (nc.Network.Name == "" || !stringInSlice(nc.Network.Name, configureNetworks)) { | 
					
						
							|  |  |  | 			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) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if len(configureNetworks) > 0 && (cl.Name == "" || !stringInSlice(cl.Name, configureNetworks)) { | 
					
						
							|  |  |  | 			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-03-21 23:19:47 +08:00
										 |  |  | func runCopyStdio(stdio *sync.WaitGroup, copyStdio bool, stdioPipe [][]int, copyConsole bool, consoleListener *net.UnixListener, finishCopy []int, finishedCopy chan struct{}) { | 
					
						
							|  |  |  | 	defer func() { | 
					
						
							|  |  |  | 		unix.Close(finishCopy[0]) | 
					
						
							|  |  |  | 		if copyStdio { | 
					
						
							|  |  |  | 			unix.Close(stdioPipe[unix.Stdin][1]) | 
					
						
							|  |  |  | 			unix.Close(stdioPipe[unix.Stdout][0]) | 
					
						
							|  |  |  | 			unix.Close(stdioPipe[unix.Stderr][0]) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		stdio.Done() | 
					
						
							|  |  |  | 		finishedCopy <- struct{}{} | 
					
						
							|  |  |  | 	}() | 
					
						
							|  |  |  | 	// If we're not doing I/O handling, we're done.
 | 
					
						
							|  |  |  | 	if !copyConsole && !copyStdio { | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	terminalFD := -1 | 
					
						
							|  |  |  | 	if copyConsole { | 
					
						
							|  |  |  | 		// Accept a connection over our listening socket.
 | 
					
						
							|  |  |  | 		fd, err := runAcceptTerminal(consoleListener) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			logrus.Errorf("%v", err) | 
					
						
							|  |  |  | 			return | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		terminalFD = fd | 
					
						
							|  |  |  | 		// Set our terminal's mode to raw, to pass handling of special
 | 
					
						
							|  |  |  | 		// terminal input to the terminal in the container.
 | 
					
						
							|  |  |  | 		state, err := terminal.MakeRaw(unix.Stdin) | 
					
						
							|  |  |  | 		if 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) | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			}() | 
					
						
							|  |  |  | 			// FIXME - if we're connected to a terminal, we should be
 | 
					
						
							|  |  |  | 			// passing the updated terminal size down when we receive a
 | 
					
						
							|  |  |  | 			// SIGWINCH.
 | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	// Track how many descriptors we're expecting data from.
 | 
					
						
							|  |  |  | 	reading := 0 | 
					
						
							|  |  |  | 	// Map describing where data on an incoming descriptor should go.
 | 
					
						
							|  |  |  | 	relayMap := make(map[int]int) | 
					
						
							|  |  |  | 	// Map describing incoming descriptors.
 | 
					
						
							|  |  |  | 	relayDesc := make(map[int]string) | 
					
						
							|  |  |  | 	// Buffers.
 | 
					
						
							|  |  |  | 	relayBuffer := make(map[int]*bytes.Buffer) | 
					
						
							|  |  |  | 	if copyConsole { | 
					
						
							|  |  |  | 		// Input from our stdin, output from the terminal descriptor.
 | 
					
						
							|  |  |  | 		relayMap[unix.Stdin] = terminalFD | 
					
						
							|  |  |  | 		relayDesc[unix.Stdin] = "stdin" | 
					
						
							|  |  |  | 		relayBuffer[unix.Stdin] = new(bytes.Buffer) | 
					
						
							|  |  |  | 		relayMap[terminalFD] = unix.Stdout | 
					
						
							|  |  |  | 		relayDesc[terminalFD] = "container terminal output" | 
					
						
							|  |  |  | 		relayBuffer[terminalFD] = new(bytes.Buffer) | 
					
						
							|  |  |  | 		reading = 2 | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if copyStdio { | 
					
						
							|  |  |  | 		// Input from our stdin, output from the stdout and stderr pipes.
 | 
					
						
							|  |  |  | 		relayMap[unix.Stdin] = stdioPipe[unix.Stdin][1] | 
					
						
							|  |  |  | 		relayDesc[unix.Stdin] = "stdin" | 
					
						
							|  |  |  | 		relayBuffer[unix.Stdin] = new(bytes.Buffer) | 
					
						
							|  |  |  | 		relayMap[stdioPipe[unix.Stdout][0]] = unix.Stdout | 
					
						
							|  |  |  | 		relayDesc[stdioPipe[unix.Stdout][0]] = "container stdout" | 
					
						
							|  |  |  | 		relayBuffer[stdioPipe[unix.Stdout][0]] = new(bytes.Buffer) | 
					
						
							|  |  |  | 		relayMap[stdioPipe[unix.Stderr][0]] = unix.Stderr | 
					
						
							|  |  |  | 		relayDesc[stdioPipe[unix.Stderr][0]] = "container stderr" | 
					
						
							|  |  |  | 		relayBuffer[stdioPipe[unix.Stderr][0]] = new(bytes.Buffer) | 
					
						
							|  |  |  | 		reading = 3 | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	// Set our reading descriptors to non-blocking.
 | 
					
						
							|  |  |  | 	for fd := range relayMap { | 
					
						
							|  |  |  | 		if err := unix.SetNonblock(fd, true); err != nil { | 
					
						
							|  |  |  | 			logrus.Errorf("error setting %s to nonblocking: %v", relayDesc[fd], err) | 
					
						
							|  |  |  | 			return | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	// Pass data back and forth.
 | 
					
						
							|  |  |  | 	for { | 
					
						
							|  |  |  | 		// Start building the list of descriptors to poll.
 | 
					
						
							|  |  |  | 		pollFds := make([]unix.PollFd, 0, reading+1) | 
					
						
							|  |  |  | 		// 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.
 | 
					
						
							|  |  |  | 		nevents, err := unix.Poll(pollFds, -1) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			if errno, isErrno := err.(syscall.Errno); isErrno { | 
					
						
							|  |  |  | 				switch errno { | 
					
						
							|  |  |  | 				case syscall.EINTR: | 
					
						
							|  |  |  | 					continue | 
					
						
							|  |  |  | 				default: | 
					
						
							|  |  |  | 					logrus.Errorf("unable to wait for stdio/terminal data to relay: %v", err) | 
					
						
							|  |  |  | 					return | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			} else { | 
					
						
							|  |  |  | 				logrus.Errorf("unable to wait for stdio/terminal data to relay: %v", err) | 
					
						
							|  |  |  | 				return | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if nevents == 0 { | 
					
						
							|  |  |  | 			logrus.Errorf("unexpected no data, no error waiting for terminal data to relay") | 
					
						
							|  |  |  | 			return | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		var removes []int | 
					
						
							|  |  |  | 		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 { | 
					
						
							|  |  |  | 				removes = append(removes, int(pollFd.Fd)) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			// If the EPOLLIN flag isn't set, then there's no data to be read from this descriptor.
 | 
					
						
							|  |  |  | 			if pollFd.Revents&unix.POLLIN == 0 { | 
					
						
							|  |  |  | 				// If we're using pipes and it's our stdin, close the writing end
 | 
					
						
							|  |  |  | 				// of the corresponding pipe.
 | 
					
						
							|  |  |  | 				if copyStdio && int(pollFd.Fd) == unix.Stdin { | 
					
						
							|  |  |  | 					unix.Close(stdioPipe[unix.Stdin][1]) | 
					
						
							|  |  |  | 					stdioPipe[unix.Stdin][1] = -1 | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				continue | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			// Copy whatever we read to wherever it needs to be sent.
 | 
					
						
							|  |  |  | 			readFD := int(pollFd.Fd) | 
					
						
							|  |  |  | 			writeFD, needToRelay := relayMap[readFD] | 
					
						
							|  |  |  | 			if needToRelay { | 
					
						
							|  |  |  | 				n, err := unix.Read(readFD, buf) | 
					
						
							|  |  |  | 				if err != nil { | 
					
						
							|  |  |  | 					if errno, isErrno := err.(syscall.Errno); isErrno { | 
					
						
							|  |  |  | 						switch errno { | 
					
						
							|  |  |  | 						default: | 
					
						
							|  |  |  | 							logrus.Errorf("unable to read %s: %v", relayDesc[readFD], err) | 
					
						
							|  |  |  | 						case syscall.EINTR, syscall.EAGAIN: | 
					
						
							|  |  |  | 						} | 
					
						
							|  |  |  | 					} else { | 
					
						
							|  |  |  | 						logrus.Errorf("unable to wait for %s data to relay: %v", relayDesc[readFD], err) | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 					continue | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				// 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.
 | 
					
						
							|  |  |  | 				if n == 0 && copyStdio && int(pollFd.Fd) == unix.Stdin { | 
					
						
							|  |  |  | 					unix.Close(stdioPipe[unix.Stdin][1]) | 
					
						
							|  |  |  | 					stdioPipe[unix.Stdin][1] = -1 | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				if n > 0 { | 
					
						
							|  |  |  | 					// Buffer the data in case we're blocked on where they need to go.
 | 
					
						
							|  |  |  | 					relayBuffer[readFD].Write(buf[:n]) | 
					
						
							|  |  |  | 					// Try to drain the buffer.
 | 
					
						
							|  |  |  | 					n, err = unix.Write(writeFD, relayBuffer[readFD].Bytes()) | 
					
						
							|  |  |  | 					if err != nil { | 
					
						
							|  |  |  | 						logrus.Errorf("unable to write %s: %v", relayDesc[readFD], err) | 
					
						
							|  |  |  | 						return | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 					relayBuffer[readFD].Next(n) | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		// Remove any descriptors which we don't need to poll any more from the poll descriptor list.
 | 
					
						
							|  |  |  | 		for _, remove := range removes { | 
					
						
							|  |  |  | 			delete(relayMap, remove) | 
					
						
							|  |  |  | 			reading-- | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if reading == 0 { | 
					
						
							|  |  |  | 			// We have no more open descriptors to read, so we can stop now.
 | 
					
						
							|  |  |  | 			return | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		// 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 | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func runAcceptTerminal(consoleListener *net.UnixListener) (int, error) { | 
					
						
							|  |  |  | 	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") | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	// Set the pseudoterminal's size to match our own.
 | 
					
						
							|  |  |  | 	winsize, err := unix.IoctlGetWinsize(unix.Stdin, unix.TIOCGWINSZ) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		logrus.Warnf("error reading size of controlling terminal: %v", err) | 
					
						
							|  |  |  | 		return terminalFD, nil | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	err = unix.IoctlSetWinsize(terminalFD, unix.TIOCSWINSZ, winsize) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		logrus.Warnf("error setting size of container pseudoterminal: %v", err) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return terminalFD, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func runSetDeathSig(cmd *exec.Cmd) { | 
					
						
							|  |  |  | 	if cmd.SysProcAttr == nil { | 
					
						
							|  |  |  | 		cmd.SysProcAttr = &syscall.SysProcAttr{} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if cmd.SysProcAttr.Pdeathsig == 0 { | 
					
						
							|  |  |  | 		cmd.SysProcAttr.Pdeathsig = syscall.SIGTERM | 
					
						
							| 
									
										
										
										
											2017-02-14 05:12:02 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											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 | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
											  
											
												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
										 |  |  | 
 | 
					
						
							|  |  |  | // Create and bind mount all bind-mount sources into a subdirectory of
 | 
					
						
							|  |  |  | // bundlePath that can only be reached by the root user of the container's user
 | 
					
						
							|  |  |  | // namespace.
 | 
					
						
							|  |  |  | func runSetupIntermediateMountNamespace(spec *specs.Spec, bundlePath string) (unmountAll func() error, err error) { | 
					
						
							|  |  |  | 	defer func() { | 
					
						
							|  |  |  | 		// Strip "nobuildahbind" options out of the spec, at least. */
 | 
					
						
							|  |  |  | 		for i := range spec.Mounts { | 
					
						
							|  |  |  | 			if stringInSlice("nobuildahbind", spec.Mounts[i].Options) { | 
					
						
							|  |  |  | 				prunedOptions := make([]string, 0, len(spec.Mounts[i].Options)) | 
					
						
							|  |  |  | 				for _, option := range spec.Mounts[i].Options { | 
					
						
							|  |  |  | 					if option != "nobuildahbind" { | 
					
						
							|  |  |  | 						prunedOptions = append(prunedOptions, option) | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				spec.Mounts[i].Options = prunedOptions | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	}() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Create a new mount namespace in which to do the things we're doing.
 | 
					
						
							|  |  |  | 	if err := unix.Unshare(unix.CLONE_NEWNS); err != nil { | 
					
						
							|  |  |  | 		return nil, errors.Wrapf(err, "error creating new mount namespace for %v", spec.Process.Args) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Make all of our mounts private to our namespace.
 | 
					
						
							|  |  |  | 	if err := mount.MakePrivate("/"); err != nil { | 
					
						
							|  |  |  | 		return nil, errors.Wrapf(err, "error making mounts private to mount namespace for %v", spec.Process.Args) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// We expect a root directory to be defined.
 | 
					
						
							|  |  |  | 	if spec.Root == nil { | 
					
						
							|  |  |  | 		return nil, errors.Errorf("configuration has no root filesystem?") | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	rootPath := spec.Root.Path | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Make sure the bundle directory is searchable.  We created it with
 | 
					
						
							|  |  |  | 	// TempDir(), so it should have started with permissions set to 0700.
 | 
					
						
							|  |  |  | 	info, err := os.Stat(bundlePath) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, errors.Wrapf(err, "error checking permissions on %q", bundlePath) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if err = os.Chmod(bundlePath, info.Mode()|0111); err != nil { | 
					
						
							|  |  |  | 		return nil, errors.Wrapf(err, "error loosening permissions on %q", bundlePath) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Figure out who needs to be able to reach these bind mounts in order
 | 
					
						
							|  |  |  | 	// for the container to be started.
 | 
					
						
							|  |  |  | 	rootUID, rootGID, err := getHostRootIDs(spec) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Hand back a callback that the caller can use to clean up everything
 | 
					
						
							|  |  |  | 	// we're doing here.
 | 
					
						
							|  |  |  | 	unmount := []string{} | 
					
						
							|  |  |  | 	unmountAll = func() (err error) { | 
					
						
							|  |  |  | 		for _, mountpoint := range unmount { | 
					
						
							|  |  |  | 			subdirs := []string{mountpoint} | 
					
						
							|  |  |  | 			var infos []*mount.Info | 
					
						
							|  |  |  | 			infos, err = mount.GetMounts() | 
					
						
							|  |  |  | 			// Gather up mountpoints below this one, since we did
 | 
					
						
							|  |  |  | 			// some recursive mounting.
 | 
					
						
							|  |  |  | 			if err == nil { | 
					
						
							|  |  |  | 				for _, info := range infos { | 
					
						
							|  |  |  | 					if strings.HasPrefix(info.Mountpoint, mountpoint) { | 
					
						
							|  |  |  | 						subdirs = append(subdirs, info.Mountpoint) | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			// Unmount all of the lower mountpoints, then the
 | 
					
						
							|  |  |  | 			// mountpoint itself.
 | 
					
						
							|  |  |  | 			sort.Strings(subdirs) | 
					
						
							|  |  |  | 			for i := range subdirs { | 
					
						
							|  |  |  | 				var err2 error | 
					
						
							|  |  |  | 				subdir := subdirs[len(subdirs)-i-1] | 
					
						
							|  |  |  | 				for err2 == nil { | 
					
						
							|  |  |  | 					err2 = unix.Unmount(subdir, 0) | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				if errno, ok := err2.(syscall.Errno); !ok || errno != unix.EINVAL { | 
					
						
							|  |  |  | 					logrus.Warnf("error unmounting %q: %v", mountpoint, err2) | 
					
						
							|  |  |  | 					if err == nil { | 
					
						
							|  |  |  | 						err = err2 | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			// Remove just the mountpoint.
 | 
					
						
							|  |  |  | 			if err2 := os.Remove(mountpoint); err2 != nil { | 
					
						
							|  |  |  | 				logrus.Warnf("error removing %q: %v", mountpoint, err2) | 
					
						
							|  |  |  | 				if err == nil { | 
					
						
							|  |  |  | 					err = err2 | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Create a top-level directory that the "root" user will be able to
 | 
					
						
							|  |  |  | 	// access, that "root" from containers which use different mappings, or
 | 
					
						
							|  |  |  | 	// other unprivileged users outside of containers, shouldn't be able to
 | 
					
						
							|  |  |  | 	// access.
 | 
					
						
							|  |  |  | 	mnt := filepath.Join(bundlePath, "mnt") | 
					
						
							|  |  |  | 	if err = idtools.MkdirAndChown(mnt, 0100, idtools.IDPair{UID: int(rootUID), GID: int(rootGID)}); err != nil { | 
					
						
							|  |  |  | 		return unmountAll, errors.Wrapf(err, "error creating %q owned by the container's root user", mnt) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Make that directory private, and add it to the list of locations we
 | 
					
						
							|  |  |  | 	// unmount at cleanup time.
 | 
					
						
							|  |  |  | 	if err = mount.MakeRPrivate(mnt); err != nil { | 
					
						
							|  |  |  | 		return unmountAll, errors.Wrapf(err, "error marking filesystem at %q as private", mnt) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	unmount = append([]string{mnt}, unmount...) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Create a bind mount for the root filesystem and add it to the list.
 | 
					
						
							|  |  |  | 	rootfs := filepath.Join(mnt, "rootfs") | 
					
						
							|  |  |  | 	if err = os.Mkdir(rootfs, 0000); err != nil { | 
					
						
							|  |  |  | 		return unmountAll, errors.Wrapf(err, "error creating directory %q", rootfs) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if err = unix.Mount(rootPath, rootfs, "", unix.MS_BIND|unix.MS_REC|unix.MS_PRIVATE, ""); err != nil { | 
					
						
							|  |  |  | 		return unmountAll, errors.Wrapf(err, "error bind mounting root filesystem from %q to %q", rootPath, rootfs) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	unmount = append([]string{rootfs}, unmount...) | 
					
						
							|  |  |  | 	spec.Root.Path = rootfs | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Do the same for everything we're binding in.
 | 
					
						
							|  |  |  | 	mounts := make([]specs.Mount, 0, len(spec.Mounts)) | 
					
						
							|  |  |  | 	for i := range spec.Mounts { | 
					
						
							|  |  |  | 		// If we're not using an intermediate, leave it in the list.
 | 
					
						
							|  |  |  | 		if runLeaveBindMountAlone(spec.Mounts[i]) { | 
					
						
							|  |  |  | 			mounts = append(mounts, spec.Mounts[i]) | 
					
						
							|  |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		// Check if the source is a directory or something else.
 | 
					
						
							|  |  |  | 		info, err := os.Stat(spec.Mounts[i].Source) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			if os.IsNotExist(err) { | 
					
						
							|  |  |  | 				logrus.Warnf("couldn't find %q on host to bind mount into container", spec.Mounts[i].Source) | 
					
						
							|  |  |  | 				continue | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			return unmountAll, errors.Wrapf(err, "error checking if %q is a directory", spec.Mounts[i].Source) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		stage := filepath.Join(mnt, fmt.Sprintf("buildah-bind-target-%d", i)) | 
					
						
							|  |  |  | 		if info.IsDir() { | 
					
						
							|  |  |  | 			// If the source is a directory, make one to use as the
 | 
					
						
							|  |  |  | 			// mount target.
 | 
					
						
							|  |  |  | 			if err = os.Mkdir(stage, 0000); err != nil { | 
					
						
							|  |  |  | 				return unmountAll, errors.Wrapf(err, "error creating directory %q", stage) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} else { | 
					
						
							|  |  |  | 			// If the source is not a directory, create an empty
 | 
					
						
							|  |  |  | 			// file to use as the mount target.
 | 
					
						
							|  |  |  | 			file, err := os.OpenFile(stage, os.O_WRONLY|os.O_CREATE, 0000) | 
					
						
							|  |  |  | 			if err != nil { | 
					
						
							|  |  |  | 				return unmountAll, errors.Wrapf(err, "error creating file %q", stage) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			file.Close() | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		// Bind mount the source from wherever it is to a place where
 | 
					
						
							|  |  |  | 		// we know the runtime helper will be able to get to it...
 | 
					
						
							|  |  |  | 		if err = unix.Mount(spec.Mounts[i].Source, stage, "", unix.MS_BIND|unix.MS_REC|unix.MS_PRIVATE, ""); err != nil { | 
					
						
							|  |  |  | 			return unmountAll, errors.Wrapf(err, "error bind mounting bind object from %q to %q", spec.Mounts[i].Source, stage) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		spec.Mounts[i].Source = stage | 
					
						
							|  |  |  | 		// ... and update the source location that we'll pass to the
 | 
					
						
							|  |  |  | 		// runtime to our intermediate location.
 | 
					
						
							|  |  |  | 		mounts = append(mounts, spec.Mounts[i]) | 
					
						
							|  |  |  | 		unmount = append([]string{stage}, unmount...) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	spec.Mounts = mounts | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return unmountAll, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Decide if the mount should not be redirected to an intermediate location first.
 | 
					
						
							|  |  |  | func runLeaveBindMountAlone(mount specs.Mount) bool { | 
					
						
							|  |  |  | 	// If we know we shouldn't do a redirection for this mount, skip it.
 | 
					
						
							|  |  |  | 	if stringInSlice("nobuildahbind", mount.Options) { | 
					
						
							|  |  |  | 		return true | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	// If we're not bind mounting it in, we don't need to do anything for it.
 | 
					
						
							|  |  |  | 	if mount.Type != "bind" && !stringInSlice("bind", mount.Options) && !stringInSlice("rbind", mount.Options) { | 
					
						
							|  |  |  | 		return true | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return false | 
					
						
							|  |  |  | } |