X-mount.idmap: how to specify ID-mapping directly

Hi there !

I’m trying to have a (unprivileged, started by root) lxc container having access to my home directory in the host. The uid/gid (1000/1000) of this host home directory is not mapped with lxc.idmap in the container, for security reasons.

On the host:

# ls -ldn /home/gfa/
drwx------ 1 1000 1000 196 Sep 14 15:29 /home/gfa/

LXC config file:

lxc.idmap = u 0 100000 100000
lxc.idmap = g 0 100000 100000
lxc.mount.entry = /home/gfa mnt none bind,X-mount.idmap=container 0 0
...

I tried to use the idmapped / X-mount.idmap feature so that the host home directory, bind mounted to /mnt, could be accessed from the container, i.e. would, in the container, belong to some user, and not to nobody:nogroup.

This works fine, except I cannot choose the uid/guid mapping. It seems to use the mapping defined by the lxc.idmapentries, which is quite inconvenient if the uid/gid in the host is not the same uid/gid (mapped with lxc.idmap) in the container.

So I tried to specify the mount idmap mapping directly, as documented by mount man page, but starting the container fails, like if this feature was not supported:
# lxc-start -n my-container
lxc-start: my-container: ../src/lxc/conf.c: parse_lxc_mount_attrs: 2343 No such file or directory - Failed to open user namespace “u:1000:1001:1”
lxc-start: my-container: ../src/lxc/conf.c: lxc_setup: 3923 Failed to setup mount entries

lxc config file:
lxc.idmap = u 0 100000 100000
lxc.idmap = g 0 100000 100000
lxc.mount.entry = /home/gfa mnt none bind,idmap=“u:1000:1001:1” 0 0
...
And, indeed, the lxc documentation says we can set this value to containeror to another /proc/<pid>/ns/userfile, but not to a direct mapping… Why this feature get stripped ?

I tried to workaround this by calling mount from a script called via lxc.hook.mountbut I get a permission error :

LXC config file:

lxc.idmap = u 0 100000 100000
lxc.idmap = g 0 100000 100000
lxc.hook.mount = /var/lib/lxc/my-container/idmap_home_gfa.sh
...

/var/lib/lxc/my-container/idmap_home_gfa.sh script:

#!/usr/bin/env bash
/usr/bin/id
echo “ls -ld /home/gfa”
ls -ld /home/gfa
echo “ls -ld ${LXC_ROOTFS_MOUNT}”
ls -ld ${LXC_ROOTFS_MOUNT}
echo “mount -o bind,X-mount.idmap=b:1000:10000000:1 /home/gfa ${LXC_ROOTFS_MOUNT}/mnt”
mount -o bind,X-mount.idmap=b:1000:10000000:1 /home/gfa ${LXC_ROOTFS_MOUNT}/mnt

And I get something like this in the logs:

lxc-start my-container 20250914151913.106 INFO utils - ../src/lxc/utils.c:run_script_argv:590 - Executing script “/usr/share/lxcfs/lxc.mount.hook” for container “my-container”
lxc-start my-container 20250914151913.200 INFO utils - ../src/lxc/utils.c:run_script_argv:590 - Executing script “/var/lib/lxc/my-container/idmap_home_gfa.sh” for container “my-container”
lxc-start my-container 20250914151913.218 DEBUG utils - ../src/lxc/utils.c:run_buffer:560 - Script exec /var/lib/lxc/my-container/idmap_home_gfa.sh produced output: uid=0(root) gid=0(root) groups=0(root)
lxc-start my-container 20250914151913.218 DEBUG utils - ../src/lxc/utils.c:run_buffer:560 - Script exec /var/lib/lxc/my-container/idmap_home_gfa.sh produced output: ls -ld /home/gfa
lxc-start my-container 20250914151913.223 DEBUG utils - ../src/lxc/utils.c:run_buffer:560 - Script exec /var/lib/lxc/my-container/idmap_home_gfa.sh produced output: drwx------ 1 nobody nogroup 196 Sep 14 15:29 /home/gfa
lxc-start my-container 20250914151913.224 DEBUG utils - ../src/lxc/utils.c:run_buffer:560 - Script exec /var/lib/lxc/my-container/idmap_home_gfa.sh produced output: ls -ld /usr/lib/x86_64-linux-gnu/lxc/rootfs
lxc-start my-container 20250914151913.228 DEBUG utils - ../src/lxc/utils.c:run_buffer:560 - Script exec /var/lib/lxc/my-container/idmap_home_gfa.sh produced output: drwxr-xr-x 1 root root 132 Aug 30 16:23 /usr/lib/x86_64-linux-gnu/lxc/rootfs
lxc-start my-container 20250914151913.229 DEBUG utils - ../src/lxc/utils.c:run_buffer:560 - Script exec /var/lib/lxc/my-container/idmap_home_gfa.sh produced output: mount -o bind,X-mount.idmap=b:1000:10000000:1 /home/gfa /usr/lib/x86_64-linux-gnu/lxc/rootfs/mnt
lxc-start my-container 20250914151913.235 DEBUG utils - ../src/lxc/utils.c:run_buffer:560 - Script exec /var/lib/lxc/my-container/idmap_home_gfa.sh produced output: mount: /usr/lib/x86_64-linux-gnu/lxc/rootfs/mnt: failed to parse mount options ‘rw,bind,X-mount.idmap=b:1000:10000000:1’: Permission denied.
lxc-start my-container 20250914151913.236 ERROR utils - ../src/lxc/utils.c:run_buffer:571 - Script exited with status 1
lxc-start my-container 20250914151913.236 ERROR conf - ../src/lxc/conf.c:lxc_setup:3948 - Failed to run mount hooks
lxc-start my-container 20250914151913.236 ERROR start - ../src/lxc/start.c:do_start:1273 - Failed to setup container “my-container”

The /home/gfa directory already belongs to nobody:nogroup when mountget called, so I guess this script is called too late, but the knowledge required to understand how to go further gets beyond mine…

I’m still searching how to idmap mount a host directory into my container with a “custom” idmap mount mapping…

I recently discovered this feature is a available and well documented in the Redhat podman container implementation.
Quoting --volume, -v=[[SOURCE-VOLUME|HOST-DIR:]CONTAINER-DIR[:OPTIONS]] — Podman documentation :
If idmap is specified, create an idmapped mount to the target user namespace in the container. The idmap option supports a custom mapping that can be different than the user namespace used by the container. The mapping can be specified after the idmap option like: idmap=uids=0-1-10#10-11-10;gids=0-100-10. For each triplet, the first value is the start of the backing file system IDs that are mapped to the second value on the host. The length of this mapping is given in the third value. Multiple ranges are separated with #.

Example :
gfa$ mkdir /tmp/mnt
gfa$ sudo touch /tmp/mnt/root
gfa$ touch /tmp/mnt/gfa
gfa$ ls -l /tmp/mnt/
-rw-rw-r-- 1 gfa gfa 0 2 sept. 21:17 gfa
-rw-r--r-- 1 root root 0 2 sept. 21:17 root

then
gfa$ sudo podman run -v /tmp/mnt:/mnt:idmap='uids=1000-0-1#0-1000-1;gids=1000-0-1#0-1000-1' --rm busybox ls -l /mnt
-rw-rw-r-- 1 root root 0 Sep 2 19:17 gfa
-rw-r--r-- 1 1000 1000 0 Sep 2 19:17 root

Here, the files belonging to the gfa user (1000:1000) in the /mnt directory (and not in any other container directory) inside the container belong to the root user in /tmp/mnt directory inside the host (and, again, not is any other host directory). And vice versa.

Anyone knows how to do that with lxc ?

mount -o bind,X-mount.idmap=b:1000:10000000:1 /home/gfa ${LXC_ROOTFS_MOUNT}/mnt
this work only if run as root

Yes, I could run this command before starting the container (via the lxc.hook.pre-start hook, probably).

But that’s not equivalent: in this case, the idmapped bind mount is also visible in the host.
This imply, for example, that some users in the host (depending on files permissions) might read or write files they are supposed to.