mirror of
https://github.com/containers/podman.git
synced 2025-07-01 16:17:06 +08:00
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>
This commit is contained in:
@ -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)
|
||||
}
|
||||
|
@ -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
|
||||
```
|
||||
|
||||
|
||||
|
@ -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()
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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 {
|
||||
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) {
|
||||
|
@ -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")
|
||||
}
|
||||
|
||||
|
@ -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"))
|
||||
})
|
||||
})
|
||||
|
Reference in New Issue
Block a user