summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaul Holzinger <paul.holzinger@web.de>2021-03-29 18:57:54 +0200
committerPaul Holzinger <paul.holzinger@web.de>2021-04-07 15:54:12 +0200
commit0a39ad196cf4af601b0ea32b2c0e0490c9079377 (patch)
treebb13c7343185c3c42356b16a4e9f55508ea6f786
parent0e67053b9a26f20e5ccbffdcc5e7a84254ca16b8 (diff)
downloadpodman-0a39ad196cf4af601b0ea32b2c0e0490c9079377.tar.gz
podman-0a39ad196cf4af601b0ea32b2c0e0490c9079377.tar.bz2
podman-0a39ad196cf4af601b0ea32b2c0e0490c9079377.zip
podman unshare: add --rootless-cni to join the ns
Add a new --rootless-cni option to podman unshare to also join the rootless-cni network namespace. This is useful if you want to connect to a rootless container via IP address. This is only possible from the rootless-cni namespace and not from the host namespace. This option also helps to debug problems in the rootless-cni namespace. Signed-off-by: Paul Holzinger <paul.holzinger@web.de>
-rw-r--r--cmd/podman/system/unshare.go6
-rw-r--r--docs/source/markdown/podman-unshare.1.md37
-rw-r--r--libpod/networking_linux.go27
-rw-r--r--pkg/domain/entities/engine_container.go2
-rw-r--r--pkg/domain/entities/system.go5
-rw-r--r--pkg/domain/infra/abi/system.go26
-rw-r--r--pkg/domain/infra/tunnel/system.go2
-rw-r--r--test/e2e/unshare_test.go7
8 files changed, 90 insertions, 22 deletions
diff --git a/cmd/podman/system/unshare.go b/cmd/podman/system/unshare.go
index 5e6ff569b..c07751532 100644
--- a/cmd/podman/system/unshare.go
+++ b/cmd/podman/system/unshare.go
@@ -12,9 +12,10 @@ import (
)
var (
+ unshareOptions = entities.SystemUnshareOptions{}
unshareDescription = "Runs a command in a modified user namespace."
unshareCommand = &cobra.Command{
- Use: "unshare [COMMAND [ARG...]]",
+ Use: "unshare [options] [COMMAND [ARG...]]",
DisableFlagsInUseLine: true,
Short: "Run a command in a modified user namespace",
Long: unshareDescription,
@@ -33,6 +34,7 @@ func init() {
})
flags := unshareCommand.Flags()
flags.SetInterspersed(false)
+ flags.BoolVar(&unshareOptions.RootlessCNI, "rootless-cni", false, "Join the rootless network namespace used for CNI networking")
}
func unshare(cmd *cobra.Command, args []string) error {
@@ -49,5 +51,5 @@ func unshare(cmd *cobra.Command, args []string) error {
args = []string{shell}
}
- return registry.ContainerEngine().Unshare(registry.Context(), args)
+ return registry.ContainerEngine().Unshare(registry.Context(), args, unshareOptions)
}
diff --git a/docs/source/markdown/podman-unshare.1.md b/docs/source/markdown/podman-unshare.1.md
index 239213981..4451ad79c 100644
--- a/docs/source/markdown/podman-unshare.1.md
+++ b/docs/source/markdown/podman-unshare.1.md
@@ -24,6 +24,19 @@ The unshare session defines two environment variables:
- **CONTAINERS_GRAPHROOT**: the path to the persistent container's data.
- **CONTAINERS_RUNROOT**: the path to the volatile container's data.
+## OPTIONS
+
+#### **\-\-help**, **-h**
+
+Print usage statement
+
+#### **\-\-rootless-cni**
+
+Join the rootless network namespace used for CNI networking. It can be used to
+connect to a rootless container via IP address (CNI networking). This is otherwise
+not possible from the host network namespace.
+_Note: Using this option with more than one unshare session can have unexpected results._
+
## EXAMPLE
```
@@ -35,6 +48,30 @@ $ podman unshare cat /proc/self/uid_map /proc/self/gid_map
1 10000 65536
0 1000 1
1 10000 65536
+
+$ podman unshare --rootless-cni ip addr
+1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
+ link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
+ inet 127.0.0.1/8 scope host lo
+ valid_lft forever preferred_lft forever
+ inet6 ::1/128 scope host
+ valid_lft forever preferred_lft forever
+2: tap0: <BROADCAST,UP,LOWER_UP> mtu 65520 qdisc fq_codel state UNKNOWN group default qlen 1000
+ link/ether 36:0e:4a:c7:45:7e brd ff:ff:ff:ff:ff:ff
+ inet 10.0.2.100/24 brd 10.0.2.255 scope global tap0
+ valid_lft forever preferred_lft forever
+ inet6 fe80::340e:4aff:fec7:457e/64 scope link
+ valid_lft forever preferred_lft forever
+3: cni-podman2: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
+ link/ether 5e:3a:71:d2:b4:3a brd ff:ff:ff:ff:ff:ff
+ inet 10.89.1.1/24 brd 10.89.1.255 scope global cni-podman2
+ valid_lft forever preferred_lft forever
+ inet6 fe80::5c3a:71ff:fed2:b43a/64 scope link
+ valid_lft forever preferred_lft forever
+4: vethd4ba3a2f@if3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master cni-podman2 state UP group default
+ link/ether 8a:c9:56:32:17:0c brd ff:ff:ff:ff:ff:ff link-netnsid 0
+ inet6 fe80::88c9:56ff:fe32:170c/64 scope link
+ valid_lft forever preferred_lft forever
```
diff --git a/libpod/networking_linux.go b/libpod/networking_linux.go
index 3c4014c73..6e2c2880f 100644
--- a/libpod/networking_linux.go
+++ b/libpod/networking_linux.go
@@ -105,13 +105,13 @@ func (r *Runtime) getPodNetwork(id, name, nsPath string, networks []string, port
return ctrNetwork
}
-type rootlessCNI struct {
+type RootlessCNI struct {
ns ns.NetNS
dir string
lock lockfile.Locker
}
-func (r *rootlessCNI) Do(toRun func() error) error {
+func (r *RootlessCNI) Do(toRun func() error) error {
err := r.ns.Do(func(_ ns.NetNS) error {
// before we can run the given function
// we have to setup all mounts correctly
@@ -174,9 +174,14 @@ func (r *rootlessCNI) Do(toRun func() error) error {
return err
}
-// cleanup the rootless cni namespace if needed
+// Cleanup the rootless cni namespace if needed
// check if we have running containers with the bridge network mode
-func (r *rootlessCNI) cleanup(runtime *Runtime) error {
+func (r *RootlessCNI) Cleanup(runtime *Runtime) error {
+ _, err := os.Stat(r.dir)
+ if os.IsNotExist(err) {
+ // the directory does not exists no need for cleanup
+ return nil
+ }
r.lock.Lock()
defer r.lock.Unlock()
running := func(c *Container) bool {
@@ -234,10 +239,10 @@ func (r *rootlessCNI) cleanup(runtime *Runtime) error {
return nil
}
-// getRootlessCNINetNs returns the rootless cni object. If create is set to true
+// GetRootlessCNINetNs returns the rootless cni object. If create is set to true
// the rootless cni namespace will be created if it does not exists already.
-func (r *Runtime) getRootlessCNINetNs(new bool) (*rootlessCNI, error) {
- var rootlessCNINS *rootlessCNI
+func (r *Runtime) GetRootlessCNINetNs(new bool) (*RootlessCNI, error) {
+ var rootlessCNINS *RootlessCNI
if rootless.IsRootless() {
runDir, err := util.GetRuntimeDir()
if err != nil {
@@ -421,7 +426,7 @@ func (r *Runtime) getRootlessCNINetNs(new bool) (*rootlessCNI, error) {
os.Setenv("PATH", path)
}
- rootlessCNINS = &rootlessCNI{
+ rootlessCNINS = &RootlessCNI{
ns: ns,
dir: cniDir,
lock: lock,
@@ -433,7 +438,7 @@ func (r *Runtime) getRootlessCNINetNs(new bool) (*rootlessCNI, error) {
// setUpOCICNIPod will set up the cni networks, on error it will also tear down the cni
// networks. If rootless it will join/create the rootless cni namespace.
func (r *Runtime) setUpOCICNIPod(podNetwork ocicni.PodNetwork) ([]ocicni.NetResult, error) {
- rootlessCNINS, err := r.getRootlessCNINetNs(true)
+ rootlessCNINS, err := r.GetRootlessCNINetNs(true)
if err != nil {
return nil, err
}
@@ -651,7 +656,7 @@ func (r *Runtime) closeNetNS(ctr *Container) error {
// Tear down a container's CNI network configuration and joins the
// rootless net ns as rootless user
func (r *Runtime) teardownOCICNIPod(podNetwork ocicni.PodNetwork) error {
- rootlessCNINS, err := r.getRootlessCNINetNs(false)
+ rootlessCNINS, err := r.GetRootlessCNINetNs(false)
if err != nil {
return err
}
@@ -665,7 +670,7 @@ func (r *Runtime) teardownOCICNIPod(podNetwork ocicni.PodNetwork) error {
// execute the cni setup in the rootless net ns
err = rootlessCNINS.Do(tearDownPod)
if err == nil {
- err = rootlessCNINS.cleanup(r)
+ err = rootlessCNINS.Cleanup(r)
}
} else {
err = tearDownPod()
diff --git a/pkg/domain/entities/engine_container.go b/pkg/domain/entities/engine_container.go
index bcab617af..f695d32fd 100644
--- a/pkg/domain/entities/engine_container.go
+++ b/pkg/domain/entities/engine_container.go
@@ -88,7 +88,7 @@ type ContainerEngine interface {
SecretRm(ctx context.Context, nameOrID []string, opts SecretRmOptions) ([]*SecretRmReport, error)
Shutdown(ctx context.Context)
SystemDf(ctx context.Context, options SystemDfOptions) (*SystemDfReport, error)
- Unshare(ctx context.Context, args []string) error
+ Unshare(ctx context.Context, args []string, options SystemUnshareOptions) error
Version(ctx context.Context) (*SystemVersionReport, error)
VolumeCreate(ctx context.Context, opts VolumeCreateOptions) (*IDOrNameResponse, error)
VolumeExists(ctx context.Context, namesOrID string) (*BoolReport, error)
diff --git a/pkg/domain/entities/system.go b/pkg/domain/entities/system.go
index 1a671d59e..31a6185dc 100644
--- a/pkg/domain/entities/system.go
+++ b/pkg/domain/entities/system.go
@@ -98,6 +98,11 @@ type SystemVersionReport struct {
Server *define.Version `json:",omitempty"`
}
+// SystemUnshareOptions describes the options for the unshare command
+type SystemUnshareOptions struct {
+ RootlessCNI bool
+}
+
type ComponentVersion struct {
types.Version
}
diff --git a/pkg/domain/infra/abi/system.go b/pkg/domain/infra/abi/system.go
index a3e753384..f87f9e370 100644
--- a/pkg/domain/infra/abi/system.go
+++ b/pkg/domain/infra/abi/system.go
@@ -390,13 +390,25 @@ func unshareEnv(graphroot, runroot string) []string {
fmt.Sprintf("CONTAINERS_RUNROOT=%s", runroot))
}
-func (ic *ContainerEngine) Unshare(ctx context.Context, args []string) error {
- cmd := exec.Command(args[0], args[1:]...)
- cmd.Env = unshareEnv(ic.Libpod.StorageConfig().GraphRoot, ic.Libpod.StorageConfig().RunRoot)
- cmd.Stdin = os.Stdin
- cmd.Stdout = os.Stdout
- cmd.Stderr = os.Stderr
- return cmd.Run()
+func (ic *ContainerEngine) Unshare(ctx context.Context, args []string, options entities.SystemUnshareOptions) error {
+ unshare := func() error {
+ cmd := exec.Command(args[0], args[1:]...)
+ cmd.Env = unshareEnv(ic.Libpod.StorageConfig().GraphRoot, ic.Libpod.StorageConfig().RunRoot)
+ cmd.Stdin = os.Stdin
+ cmd.Stdout = os.Stdout
+ cmd.Stderr = os.Stderr
+ return cmd.Run()
+ }
+
+ if options.RootlessCNI {
+ rootlesscni, err := ic.Libpod.GetRootlessCNINetNs(true)
+ if err != nil {
+ return err
+ }
+ defer rootlesscni.Cleanup(ic.Libpod)
+ return rootlesscni.Do(unshare)
+ }
+ return unshare()
}
func (ic ContainerEngine) Version(ctx context.Context) (*entities.SystemVersionReport, error) {
diff --git a/pkg/domain/infra/tunnel/system.go b/pkg/domain/infra/tunnel/system.go
index d2c5063c9..7400d3771 100644
--- a/pkg/domain/infra/tunnel/system.go
+++ b/pkg/domain/infra/tunnel/system.go
@@ -28,7 +28,7 @@ func (ic *ContainerEngine) SystemDf(ctx context.Context, options entities.System
return system.DiskUsage(ic.ClientCtx, nil)
}
-func (ic *ContainerEngine) Unshare(ctx context.Context, args []string) error {
+func (ic *ContainerEngine) Unshare(ctx context.Context, args []string, options entities.SystemUnshareOptions) error {
return errors.New("unshare is not supported on remote clients")
}
diff --git a/test/e2e/unshare_test.go b/test/e2e/unshare_test.go
index 515b3a42e..24ab98916 100644
--- a/test/e2e/unshare_test.go
+++ b/test/e2e/unshare_test.go
@@ -49,4 +49,11 @@ var _ = Describe("Podman unshare", func() {
ok, _ := session.GrepString(userNS)
Expect(ok).To(BeFalse())
})
+
+ It("podman unshare --rootles-cni", func() {
+ session := podmanTest.Podman([]string{"unshare", "--rootless-cni", "ip", "addr"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+ Expect(session.OutputToString()).To(ContainSubstring("tap0"))
+ })
})