podman: handle namespaces specified on the CLI

and handle differently the user namespace as it supports additional
options.

Signed-off-by: Giuseppe Scrivano <gscrivan@redhat.com>
This commit is contained in:
Giuseppe Scrivano
2020-04-22 12:38:19 +02:00
parent 2fd6a84c09
commit 48530acbd9
6 changed files with 88 additions and 57 deletions

View File

@ -49,8 +49,9 @@ func GetCreateFlags(cf *ContainerCLIOpts) *pflag.FlagSet {
"cap-drop", []string{}, "cap-drop", []string{},
"Drop capabilities from the container", "Drop capabilities from the container",
) )
cgroupNS := ""
createFlags.StringVar( createFlags.StringVar(
&cf.CGroupsNS, &cgroupNS,
"cgroupns", containerConfig.CgroupNS(), "cgroupns", containerConfig.CgroupNS(),
"cgroup namespace to use", "cgroup namespace to use",
) )
@ -247,8 +248,9 @@ func GetCreateFlags(cf *ContainerCLIOpts) *pflag.FlagSet {
"interactive", "i", false, "interactive", "i", false,
"Keep STDIN open even if not attached", "Keep STDIN open even if not attached",
) )
ipcNS := ""
createFlags.StringVar( createFlags.StringVar(
&cf.IPC, &ipcNS,
"ipc", containerConfig.IPCNS(), "ipc", containerConfig.IPCNS(),
"IPC namespace to use", "IPC namespace to use",
) )
@ -329,8 +331,9 @@ func GetCreateFlags(cf *ContainerCLIOpts) *pflag.FlagSet {
"use `OS` instead of the running OS for choosing images", "use `OS` instead of the running OS for choosing images",
) )
// markFlagHidden(createFlags, "override-os") // markFlagHidden(createFlags, "override-os")
pid := ""
createFlags.StringVar( createFlags.StringVar(
&cf.PID, &pid,
"pid", containerConfig.PidNS(), "pid", containerConfig.PidNS(),
"PID namespace to use", "PID namespace to use",
) )
@ -461,13 +464,15 @@ func GetCreateFlags(cf *ContainerCLIOpts) *pflag.FlagSet {
"user", "u", "", "user", "u", "",
"Username or UID (format: <name|uid>[:<group|gid>])", "Username or UID (format: <name|uid>[:<group|gid>])",
) )
userNS := ""
createFlags.StringVar( createFlags.StringVar(
&cf.UserNS, &userNS,
"userns", containerConfig.Containers.UserNS, "userns", containerConfig.Containers.UserNS,
"User namespace to use", "User namespace to use",
) )
utsNS := ""
createFlags.StringVar( createFlags.StringVar(
&cf.UTS, &utsNS,
"uts", containerConfig.Containers.UTSNS, "uts", containerConfig.Containers.UTSNS,
"UTS namespace to use", "UTS namespace to use",
) )

View File

@ -222,55 +222,28 @@ func FillOutSpecGen(s *specgen.SpecGenerator, c *ContainerCLIOpts, args []string
s.PortMappings = ep s.PortMappings = ep
s.Pod = c.Pod s.Pod = c.Pod
//s.CgroupNS = specgen.Namespace{ for k, v := range map[string]*specgen.Namespace{
// NSMode: , c.IPC: &s.IpcNS,
// Value: "", c.PID: &s.PidNS,
//} c.UTS: &s.UtsNS,
c.CGroupsNS: &s.CgroupNS,
//s.UserNS = specgen.Namespace{} } {
if k != "" {
// Kernel Namespaces *v, err = specgen.ParseNamespace(k)
// TODO Fix handling of namespace from pod if err != nil {
// Instead of integrating here, should be done in libpod return err
// However, that also involves setting up security opts }
// when the pod's namespace is integrated }
//namespaces = map[string]string{ }
// "cgroup": c.CGroupsNS, // userns must be treated differently
// "pid": c.PID, if c.UserNS != "" {
// //"net": c.Net.Network.Value, // TODO need help here s.UserNS, err = specgen.ParseUserNamespace(c.UserNS)
// "ipc": c.IPC, if err != nil {
// "user": c.User, return err
// "uts": c.UTS, }
//} }
// if c.Net != nil {
//if len(c.PID) > 0 { s.NetNS = c.Net.Network
// split := strings.SplitN(c.PID, ":", 2)
// // need a way to do thsi
// specgen.Namespace{
// NSMode: split[0],
// }
// //Value: split1 if len allows
//}
// TODO this is going to have be done after things like pod creation are done because
// pod creation changes these values.
//pidMode := ns.PidMode(namespaces["pid"])
//usernsMode := ns.UsernsMode(namespaces["user"])
//utsMode := ns.UTSMode(namespaces["uts"])
//cgroupMode := ns.CgroupMode(namespaces["cgroup"])
//ipcMode := ns.IpcMode(namespaces["ipc"])
//// Make sure if network is set to container namespace, port binding is not also being asked for
//netMode := ns.NetworkMode(namespaces["net"])
//if netMode.IsContainer() {
// if len(portBindings) > 0 {
// return nil, errors.Errorf("cannot set port bindings on an existing container network namespace")
// }
//}
// TODO Remove when done with namespaces for realz
// Setting a default for IPC to get this working
s.IpcNS = specgen.Namespace{
NSMode: specgen.Private,
Value: "",
} }
// TODO this is going to have to be done the libpod/server end of things // TODO this is going to have to be done the libpod/server end of things

View File

@ -149,6 +149,21 @@ func createInit(c *cobra.Command) error {
if c.Flag("no-hosts").Changed && c.Flag("add-host").Changed { if c.Flag("no-hosts").Changed && c.Flag("add-host").Changed {
return errors.Errorf("--no-hosts and --add-host cannot be set together") return errors.Errorf("--no-hosts and --add-host cannot be set together")
} }
if c.Flag("userns").Changed {
cliVals.UserNS = c.Flag("userns").Value.String()
}
if c.Flag("ipc").Changed {
cliVals.IPC = c.Flag("ipc").Value.String()
}
if c.Flag("uts").Changed {
cliVals.UTS = c.Flag("uts").Value.String()
}
if c.Flag("pid").Changed {
cliVals.PID = c.Flag("pid").Value.String()
}
if c.Flag("cgroupns").Changed {
cliVals.CGroupsNS = c.Flag("cgroupns").Value.String()
}
// Docker-compatibility: the "-h" flag for run/create is reserved for // Docker-compatibility: the "-h" flag for run/create is reserved for
// the hostname (see https://github.com/containers/libpod/issues/1367). // the hostname (see https://github.com/containers/libpod/issues/1367).

View File

@ -129,7 +129,7 @@ func (s *SpecGenerator) Validate() error {
if err := s.CgroupNS.validate(); err != nil { if err := s.CgroupNS.validate(); err != nil {
return err return err
} }
if err := s.UserNS.validate(); err != nil { if err := validateUserNS(&s.UserNS); err != nil {
return err return err
} }

View File

@ -58,8 +58,7 @@ func GetDefaultNamespaceMode(nsType string, cfg *config.Config, pod *libpod.Pod)
case "uts": case "uts":
return specgen.ParseNamespace(cfg.Containers.UTSNS) return specgen.ParseNamespace(cfg.Containers.UTSNS)
case "user": case "user":
// TODO: This may not work for --userns=auto return specgen.ParseUserNamespace(cfg.Containers.UserNS)
return specgen.ParseNamespace(cfg.Containers.UserNS)
case "net": case "net":
ns, _, err := specgen.ParseNetworkNamespace(cfg.Containers.NetNS) ns, _, err := specgen.ParseNetworkNamespace(cfg.Containers.NetNS)
return ns, err return ns, err

View File

@ -33,6 +33,11 @@ const (
// Slirp indicates that a slirp4netns network stack should // Slirp indicates that a slirp4netns network stack should
// be used // be used
Slirp NamespaceMode = "slirp4netns" Slirp NamespaceMode = "slirp4netns"
// KeepId indicates a user namespace to keep the owner uid inside
// of the namespace itself
KeepID NamespaceMode = "keep-id"
// KeepId indicates to automatically create a user namespace
Auto NamespaceMode = "auto"
) )
// Namespace describes the namespace // Namespace describes the namespace
@ -71,6 +76,16 @@ func (n *Namespace) IsPod() bool {
func (n *Namespace) IsPrivate() bool { func (n *Namespace) IsPrivate() bool {
return n.NSMode == Private return n.NSMode == Private
} }
func validateUserNS(n *Namespace) error {
if n == nil {
return nil
}
switch n.NSMode {
case Auto, KeepID:
return nil
}
return n.validate()
}
func validateNetNS(n *Namespace) error { func validateNetNS(n *Namespace) error {
if n == nil { if n == nil {
@ -158,6 +173,30 @@ func ParseNamespace(ns string) (Namespace, error) {
return toReturn, nil return toReturn, nil
} }
// ParseUserNamespace parses a user namespace specification in string
// form.
func ParseUserNamespace(ns string) (Namespace, error) {
toReturn := Namespace{}
switch {
case ns == "auto":
toReturn.NSMode = Auto
return toReturn, nil
case strings.HasPrefix(ns, "auto:"):
split := strings.SplitN(ns, ":", 2)
if len(split) != 2 {
return toReturn, errors.Errorf("invalid setting for auto: mode")
}
toReturn.NSMode = KeepID
toReturn.Value = split[1]
return toReturn, nil
case ns == "keep-id":
toReturn.NSMode = KeepID
toReturn.NSMode = FromContainer
return toReturn, nil
}
return ParseNamespace(ns)
}
// ParseNetworkNamespace parses a network namespace specification in string // ParseNetworkNamespace parses a network namespace specification in string
// form. // form.
// Returns a namespace and (optionally) a list of CNI networks to join. // Returns a namespace and (optionally) a list of CNI networks to join.