summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cmd/podman/common/create.go8
-rw-r--r--docs/source/markdown/podman-create.1.md5
-rw-r--r--docs/source/markdown/podman-run.1.md6
-rw-r--r--libpod/container_config.go4
-rw-r--r--libpod/container_inspect.go1
-rw-r--r--libpod/container_internal_linux.go35
-rw-r--r--libpod/define/container_inspect.go4
-rw-r--r--libpod/networking_slirp4netns.go40
-rw-r--r--libpod/options.go15
-rw-r--r--pkg/bindings/containers/checkpoint.go2
-rw-r--r--pkg/bindings/containers/types.go2
-rw-r--r--pkg/bindings/containers/types_restore_options.go14
-rw-r--r--pkg/domain/entities/pods.go1
-rw-r--r--pkg/domain/infra/tunnel/containers.go2
-rw-r--r--pkg/specgen/generate/container_create.go4
-rw-r--r--pkg/specgen/specgen.go4
-rw-r--r--pkg/specgenutil/specgen.go3
-rw-r--r--test/e2e/create_test.go30
18 files changed, 156 insertions, 24 deletions
diff --git a/cmd/podman/common/create.go b/cmd/podman/common/create.go
index 634b49db7..f3e2e4d6d 100644
--- a/cmd/podman/common/create.go
+++ b/cmd/podman/common/create.go
@@ -631,6 +631,14 @@ func DefineCreateFlags(cmd *cobra.Command, cf *entities.ContainerCreateOptions,
"Write the container process ID to the file")
_ = cmd.RegisterFlagCompletionFunc(pidFileFlagName, completion.AutocompleteDefault)
+ chrootDirsFlagName := "chrootdirs"
+ createFlags.StringSliceVar(
+ &cf.ChrootDirs,
+ chrootDirsFlagName, []string{},
+ "Chroot directories inside the container",
+ )
+ _ = cmd.RegisterFlagCompletionFunc(chrootDirsFlagName, completion.AutocompleteDefault)
+
if registry.IsRemote() {
_ = createFlags.MarkHidden("env-host")
_ = createFlags.MarkHidden("http-proxy")
diff --git a/docs/source/markdown/podman-create.1.md b/docs/source/markdown/podman-create.1.md
index 2a0f3b738..506f575fe 100644
--- a/docs/source/markdown/podman-create.1.md
+++ b/docs/source/markdown/podman-create.1.md
@@ -1453,6 +1453,11 @@ After the container is started, the location for the pidfile can be discovered w
$ podman inspect --format '{{ .PidFile }}' $CID
/run/containers/storage/${storage-driver}-containers/$CID/userdata/pidfile
+#### **--chrootdirs**=*path*
+
+Path to a directory inside the container that should be treated as a `chroot` directory.
+Any Podman managed file (e.g., /etc/resolv.conf, /etc/hosts, etc/hostname) that is mounted into the root directory will be mounted into that location as well.
+Multiple directories should be separated with a comma.
## EXAMPLES
diff --git a/docs/source/markdown/podman-run.1.md b/docs/source/markdown/podman-run.1.md
index 239cf3b83..7fa7bda30 100644
--- a/docs/source/markdown/podman-run.1.md
+++ b/docs/source/markdown/podman-run.1.md
@@ -1529,6 +1529,12 @@ After the container is started, the location for the pidfile can be discovered w
$ podman inspect --format '{{ .PidFile }}' $CID
/run/containers/storage/${storage-driver}-containers/$CID/userdata/pidfile
+#### **--chrootdirs**=*path*
+
+Path to a directory inside the container that should be treated as a `chroot` directory.
+Any Podman managed file (e.g., /etc/resolv.conf, /etc/hosts, etc/hostname) that is mounted into the root directory will be mounted into that location as well.
+Multiple directories should be separated with a comma.
+
## Exit Status
The exit code from **podman run** gives information about why the container
diff --git a/libpod/container_config.go b/libpod/container_config.go
index e56f1342a..0d9cd5723 100644
--- a/libpod/container_config.go
+++ b/libpod/container_config.go
@@ -165,6 +165,10 @@ type ContainerRootFSConfig struct {
Volatile bool `json:"volatile,omitempty"`
// Passwd allows to user to override podman's passwd/group file setup
Passwd *bool `json:"passwd,omitempty"`
+ // ChrootDirs is an additional set of directories that need to be
+ // treated as root directories. Standard bind mounts will be mounted
+ // into paths relative to these directories.
+ ChrootDirs []string `json:"chroot_directories,omitempty"`
}
// ContainerSecurityConfig is an embedded sub-config providing security configuration
diff --git a/libpod/container_inspect.go b/libpod/container_inspect.go
index 3df6203e3..5fb32bd90 100644
--- a/libpod/container_inspect.go
+++ b/libpod/container_inspect.go
@@ -411,6 +411,7 @@ func (c *Container) generateInspectContainerConfig(spec *spec.Spec) *define.Insp
}
ctrConfig.Passwd = c.config.Passwd
+ ctrConfig.ChrootDirs = append(ctrConfig.ChrootDirs, c.config.ChrootDirs...)
return ctrConfig
}
diff --git a/libpod/container_internal_linux.go b/libpod/container_internal_linux.go
index 1517a7df7..75250b9b1 100644
--- a/libpod/container_internal_linux.go
+++ b/libpod/container_internal_linux.go
@@ -1811,6 +1811,17 @@ func (c *Container) getRootNetNsDepCtr() (depCtr *Container, err error) {
return depCtr, nil
}
+// Ensure standard bind mounts are mounted into all root directories (including chroot directories)
+func (c *Container) mountIntoRootDirs(mountName string, mountPath string) error {
+ c.state.BindMounts[mountName] = mountPath
+
+ for _, chrootDir := range c.config.ChrootDirs {
+ c.state.BindMounts[filepath.Join(chrootDir, mountName)] = mountPath
+ }
+
+ return nil
+}
+
// Make standard bind mounts to include in the container
func (c *Container) makeBindMounts() error {
if err := os.Chown(c.state.RunDir, c.RootUID(), c.RootGID()); err != nil {
@@ -1864,7 +1875,11 @@ func (c *Container) makeBindMounts() error {
// If it doesn't, don't copy them
resolvPath, exists := bindMounts["/etc/resolv.conf"]
if !c.config.UseImageResolvConf && exists {
- c.state.BindMounts["/etc/resolv.conf"] = resolvPath
+ err := c.mountIntoRootDirs("/etc/resolv.conf", resolvPath)
+
+ if err != nil {
+ return errors.Wrapf(err, "error assigning mounts to container %s", c.ID())
+ }
}
// check if dependency container has an /etc/hosts file.
@@ -1884,7 +1899,11 @@ func (c *Container) makeBindMounts() error {
depCtr.lock.Unlock()
// finally, save it in the new container
- c.state.BindMounts["/etc/hosts"] = hostsPath
+ err := c.mountIntoRootDirs("/etc/hosts", hostsPath)
+
+ if err != nil {
+ return errors.Wrapf(err, "error assigning mounts to container %s", c.ID())
+ }
}
if !hasCurrentUserMapped(c) {
@@ -1901,7 +1920,11 @@ func (c *Container) makeBindMounts() error {
if err != nil {
return errors.Wrapf(err, "error creating resolv.conf for container %s", c.ID())
}
- c.state.BindMounts["/etc/resolv.conf"] = newResolv
+ err = c.mountIntoRootDirs("/etc/resolv.conf", newResolv)
+
+ if err != nil {
+ return errors.Wrapf(err, "error assigning mounts to container %s", c.ID())
+ }
}
if !c.config.UseImageHosts {
@@ -2329,7 +2352,11 @@ func (c *Container) updateHosts(path string) error {
if err != nil {
return err
}
- c.state.BindMounts["/etc/hosts"] = newHosts
+
+ if err = c.mountIntoRootDirs("/etc/hosts", newHosts); err != nil {
+ return err
+ }
+
return nil
}
diff --git a/libpod/define/container_inspect.go b/libpod/define/container_inspect.go
index 804b2b143..ae2ce9724 100644
--- a/libpod/define/container_inspect.go
+++ b/libpod/define/container_inspect.go
@@ -75,6 +75,10 @@ type InspectContainerConfig struct {
StopTimeout uint `json:"StopTimeout"`
// Passwd determines whether or not podman can add entries to /etc/passwd and /etc/group
Passwd *bool `json:"Passwd,omitempty"`
+ // ChrootDirs is an additional set of directories that need to be
+ // treated as root directories. Standard bind mounts will be mounted
+ // into paths relative to these directories.
+ ChrootDirs []string `json:"ChrootDirs,omitempty"`
}
// InspectRestartPolicy holds information about the container's restart policy.
diff --git a/libpod/networking_slirp4netns.go b/libpod/networking_slirp4netns.go
index 690f0c1fa..cc44f78f7 100644
--- a/libpod/networking_slirp4netns.go
+++ b/libpod/networking_slirp4netns.go
@@ -13,6 +13,7 @@ import (
"path/filepath"
"strconv"
"strings"
+ "sync"
"syscall"
"time"
@@ -302,11 +303,15 @@ func (r *Runtime) setupSlirp4netns(ctr *Container, netns ns.NetNS) error {
cmd.Stdout = logFile
cmd.Stderr = logFile
- var slirpReadyChan (chan struct{})
-
+ var slirpReadyWg, netnsReadyWg *sync.WaitGroup
if netOptions.enableIPv6 {
- slirpReadyChan = make(chan struct{})
- defer close(slirpReadyChan)
+ // use two wait groups to make sure we set the sysctl before
+ // starting slirp and reset it only after slirp is ready
+ slirpReadyWg = &sync.WaitGroup{}
+ netnsReadyWg = &sync.WaitGroup{}
+ slirpReadyWg.Add(1)
+ netnsReadyWg.Add(1)
+
go func() {
err := ns.WithNetNSPath(netnsPath, func(_ ns.NetNS) error {
// Duplicate Address Detection slows the ipv6 setup down for 1-2 seconds.
@@ -318,23 +323,37 @@ func (r *Runtime) setupSlirp4netns(ctr *Container, netns ns.NetNS) error {
// is ready in case users rely on this sysctl.
orgValue, err := ioutil.ReadFile(ipv6ConfDefaultAcceptDadSysctl)
if err != nil {
+ netnsReadyWg.Done()
+ // on ipv6 disabled systems the sysctl does not exists
+ // so we should not error
+ if errors.Is(err, os.ErrNotExist) {
+ return nil
+ }
return err
}
err = ioutil.WriteFile(ipv6ConfDefaultAcceptDadSysctl, []byte("0"), 0644)
+ netnsReadyWg.Done()
if err != nil {
return err
}
- // wait for slirp to finish setup
- <-slirpReadyChan
+
+ // wait until slirp4nets is ready before reseting this value
+ slirpReadyWg.Wait()
return ioutil.WriteFile(ipv6ConfDefaultAcceptDadSysctl, orgValue, 0644)
})
if err != nil {
logrus.Warnf("failed to set net.ipv6.conf.default.accept_dad sysctl: %v", err)
}
}()
+
+ // wait until we set the sysctl
+ netnsReadyWg.Wait()
}
if err := cmd.Start(); err != nil {
+ if netOptions.enableIPv6 {
+ slirpReadyWg.Done()
+ }
return errors.Wrapf(err, "failed to start slirp4netns process")
}
defer func() {
@@ -344,11 +363,12 @@ func (r *Runtime) setupSlirp4netns(ctr *Container, netns ns.NetNS) error {
}
}()
- if err := waitForSync(syncR, cmd, logFile, 1*time.Second); err != nil {
- return err
+ err = waitForSync(syncR, cmd, logFile, 1*time.Second)
+ if netOptions.enableIPv6 {
+ slirpReadyWg.Done()
}
- if slirpReadyChan != nil {
- slirpReadyChan <- struct{}{}
+ if err != nil {
+ return err
}
// Set a default slirp subnet. Parsing a string with the net helper is easier than building the struct myself
diff --git a/libpod/options.go b/libpod/options.go
index 1ee4e7322..2e5454393 100644
--- a/libpod/options.go
+++ b/libpod/options.go
@@ -2036,3 +2036,18 @@ func WithVolatile() CtrCreateOption {
return nil
}
}
+
+// WithChrootDirs is an additional set of directories that need to be
+// treated as root directories. Standard bind mounts will be mounted
+// into paths relative to these directories.
+func WithChrootDirs(dirs []string) CtrCreateOption {
+ return func(ctr *Container) error {
+ if ctr.valid {
+ return define.ErrCtrFinalized
+ }
+
+ ctr.config.ChrootDirs = dirs
+
+ return nil
+ }
+}
diff --git a/pkg/bindings/containers/checkpoint.go b/pkg/bindings/containers/checkpoint.go
index 1d8c34b33..84590d052 100644
--- a/pkg/bindings/containers/checkpoint.go
+++ b/pkg/bindings/containers/checkpoint.go
@@ -79,7 +79,7 @@ func Restore(ctx context.Context, nameOrID string, options *RestoreOptions) (*en
// Open the to-be-imported archive if needed.
var r io.Reader
- if i := options.GetImportAchive(); i != "" {
+ if i := options.GetImportArchive(); i != "" {
params.Set("import", "true")
r, err = os.Open(i)
if err != nil {
diff --git a/pkg/bindings/containers/types.go b/pkg/bindings/containers/types.go
index 66b90af9b..3c8b1eefa 100644
--- a/pkg/bindings/containers/types.go
+++ b/pkg/bindings/containers/types.go
@@ -64,7 +64,7 @@ type RestoreOptions struct {
IgnoreVolumes *bool
IgnoreStaticIP *bool
IgnoreStaticMAC *bool
- ImportAchive *string
+ ImportArchive *string
Keep *bool
Name *string
TCPEstablished *bool
diff --git a/pkg/bindings/containers/types_restore_options.go b/pkg/bindings/containers/types_restore_options.go
index d2778396a..e8a0e236c 100644
--- a/pkg/bindings/containers/types_restore_options.go
+++ b/pkg/bindings/containers/types_restore_options.go
@@ -77,19 +77,19 @@ func (o *RestoreOptions) GetIgnoreStaticMAC() bool {
return *o.IgnoreStaticMAC
}
-// WithImportAchive set field ImportAchive to given value
-func (o *RestoreOptions) WithImportAchive(value string) *RestoreOptions {
- o.ImportAchive = &value
+// WithImportArchive set field ImportArchive to given value
+func (o *RestoreOptions) WithImportArchive(value string) *RestoreOptions {
+ o.ImportArchive = &value
return o
}
-// GetImportAchive returns value of field ImportAchive
-func (o *RestoreOptions) GetImportAchive() string {
- if o.ImportAchive == nil {
+// GetImportArchive returns value of field ImportArchive
+func (o *RestoreOptions) GetImportArchive() string {
+ if o.ImportArchive == nil {
var z string
return z
}
- return *o.ImportAchive
+ return *o.ImportArchive
}
// WithKeep set field Keep to given value
diff --git a/pkg/domain/entities/pods.go b/pkg/domain/entities/pods.go
index 6fb3db1b5..da93d3f8b 100644
--- a/pkg/domain/entities/pods.go
+++ b/pkg/domain/entities/pods.go
@@ -263,6 +263,7 @@ type ContainerCreateOptions struct {
Workdir string
SeccompPolicy string
PidFile string
+ ChrootDirs []string
IsInfra bool
IsClone bool
diff --git a/pkg/domain/infra/tunnel/containers.go b/pkg/domain/infra/tunnel/containers.go
index fe986361b..046c2509d 100644
--- a/pkg/domain/infra/tunnel/containers.go
+++ b/pkg/domain/infra/tunnel/containers.go
@@ -390,7 +390,7 @@ func (ic *ContainerEngine) ContainerRestore(ctx context.Context, namesOrIds []st
options.WithPublishPorts(opts.PublishPorts)
if opts.Import != "" {
- options.WithImportAchive(opts.Import)
+ options.WithImportArchive(opts.Import)
report, err := containers.Restore(ic.ClientCtx, "", options)
return []*entities.RestoreReport{report}, err
}
diff --git a/pkg/specgen/generate/container_create.go b/pkg/specgen/generate/container_create.go
index c0b23953f..8ab0eae5a 100644
--- a/pkg/specgen/generate/container_create.go
+++ b/pkg/specgen/generate/container_create.go
@@ -526,6 +526,10 @@ func createContainerOptions(ctx context.Context, rt *libpod.Runtime, s *specgen.
options = append(options, libpod.WithPidFile(s.PidFile))
}
+ if len(s.ChrootDirs) != 0 {
+ options = append(options, libpod.WithChrootDirs(s.ChrootDirs))
+ }
+
options = append(options, libpod.WithSelectedPasswordManagement(s.Passwd))
return options, nil
diff --git a/pkg/specgen/specgen.go b/pkg/specgen/specgen.go
index 7f6f79b87..27d77af9f 100644
--- a/pkg/specgen/specgen.go
+++ b/pkg/specgen/specgen.go
@@ -301,6 +301,10 @@ type ContainerStorageConfig struct {
// Volatile specifies whether the container storage can be optimized
// at the cost of not syncing all the dirty files in memory.
Volatile bool `json:"volatile,omitempty"`
+ // ChrootDirs is an additional set of directories that need to be
+ // treated as root directories. Standard bind mounts will be mounted
+ // into paths relative to these directories.
+ ChrootDirs []string `json:"chroot_directories,omitempty"`
}
// ContainerSecurityConfig is a container's security features, including
diff --git a/pkg/specgenutil/specgen.go b/pkg/specgenutil/specgen.go
index b037e14cc..b87da61fb 100644
--- a/pkg/specgenutil/specgen.go
+++ b/pkg/specgenutil/specgen.go
@@ -819,6 +819,9 @@ func FillOutSpecGen(s *specgen.SpecGenerator, c *entities.ContainerCreateOptions
if !s.UnsetEnvAll {
s.UnsetEnvAll = c.UnsetEnvAll
}
+ if len(s.ChrootDirs) == 0 || len(c.ChrootDirs) != 0 {
+ s.ChrootDirs = c.ChrootDirs
+ }
// Initcontainers
if len(s.InitContainerType) == 0 || len(c.InitContainerType) != 0 {
diff --git a/test/e2e/create_test.go b/test/e2e/create_test.go
index 6a4a394ef..339fa66d8 100644
--- a/test/e2e/create_test.go
+++ b/test/e2e/create_test.go
@@ -706,4 +706,34 @@ var _ = Describe("Podman create", func() {
Expect(create.ErrorToString()).To(ContainSubstring("cannot specify a new uid/gid map when entering a pod with an infra container"))
})
+
+ It("podman create --chrootdirs inspection test", func() {
+ session := podmanTest.Podman([]string{"create", "--chrootdirs", "/var/local/qwerty", ALPINE})
+ session.WaitWithDefaultTimeout()
+ Expect(session).Should(Exit(0))
+
+ setup := podmanTest.Podman([]string{"container", "inspect", session.OutputToString()})
+ setup.WaitWithDefaultTimeout()
+ Expect(setup).Should(Exit(0))
+
+ data := setup.InspectContainerToJSON()
+ Expect(data).To(HaveLen(1))
+ Expect(data[0].Config.ChrootDirs).To(HaveLen(1))
+ Expect(data[0].Config.ChrootDirs[0]).To(Equal("/var/local/qwerty"))
+ })
+
+ It("podman create --chrootdirs functionality test", func() {
+ session := podmanTest.Podman([]string{"create", "-t", "--chrootdirs", "/var/local/qwerty", ALPINE, "/bin/cat"})
+ session.WaitWithDefaultTimeout()
+ Expect(session).Should(Exit(0))
+ ctrID := session.OutputToString()
+
+ setup := podmanTest.Podman([]string{"start", ctrID})
+ setup.WaitWithDefaultTimeout()
+ Expect(setup).Should(Exit(0))
+
+ setup = podmanTest.Podman([]string{"exec", ctrID, "cmp", "/etc/resolv.conf", "/var/local/qwerty/etc/resolv.conf"})
+ setup.WaitWithDefaultTimeout()
+ Expect(setup).Should(Exit(0))
+ })
})