Access other container's mounts while keeping permissions

Hello,

I have multiple websites instances and each one has its own unprivileged container. Website data is at /web directory that is an unshifted mount from the host. At host I can the files are owned by some user at eg. /mnt/NVME1/webdata/site1 that represents the www-data user of the container.

In order to provide FTP access to the files of each website I’m running Pure-FTPd on the host. I can define users for FTP access, point them at the appropriate host’s folder where each website’s data is stored and tell it to read/write files with the specific UID/GID that matches the www-data of the container.

Now, I want to move Pure-FTPd to its own container, how can I keep it unprivileged and mount the folder where all the websites are stored while keeping the users UID/GID as the host seems them?

If I simply add a device like:

lxc config device add pureftpd webdata disk source=/mnt/NVME1/webdata path=/mnt/webdata

They’ll all show up as nobody:nogroup. What can I do here?

Thank you.

1 Like

I want to know how you solved it. Thank you.

Jokes aside, I have no fix for this and it kinda went into my backlog for now.

You may find the following useful. It shows how to use storage volumes to store there the content that you want to share with other containers.

Thanks for the share, that links assumes you can get users and ids to align across containers and that isn’t always the case unfortunately.

The default user per container has a UID of 1000. That’s the case for images:ubuntu/22.04 or images:debian/12, probably others. Also, www-data is the same across containers.

You deal with permissions only on the webdev container.

Care to explain why it would not work? That is, describe in more detail your workflow.

In theory what you suggested works, but I’ve a mix of generic Debian containers that do have the same www-data across all of them but also alpine containers that have different IDs for that user. Also one or two custom containers with a higher user for web.

Also, currently I’ve a “webdata” folder for each container that serves websites. And from the host perspective it looks like this:

/mnt/NVME1/ct1/webdata 
  -- site1.com - 952068:952069
  -- site2.com - 952068:952069
/mnt/NVME1/ct2/webdata
  -- example1.org - 2262788:2262789
  -- example2.org - 2262788:2262789

If I run the Pure-FTPd instance on the host it has options to set a UID/GID for each FTP user so I can align them with those users and groups above. Enforcing the UID/GID on Pure-FTPd login will also make things safer as one user will always be restricted to it’s files.

This is why I wanted to have a way to allow the Pure-FTPd container to be able to interact with all those users and groups in the same way the host can.

Thank you.

try:
lxc config device add pureftpd webdata disk source=/mnt/NVME1/webdata path=/mnt/webdata shift=true

Taking on this:

/mnt/NVME1/ct1/webdata 
  -- site1.com - 952068:952069
  -- site2.com - 952068:952069
/mnt/NVME1/ct2/webdata
  -- example1.org - 2262788:2262789
  -- example2.org - 2262788:2262789

If I try to mount /mnt/NVME1 on my pureftpd container:

lxc config device add pureftpd data disk source=/mnt/NVME1 path=/mnt/data shift=true

This is what I’ll see inside the container:

root@pureftpd:~# ls -la /mnt/data/ct1/webdata
total 0
drwxr-xr-x 1 nobody nogroup  52 Sep 20 20:53 .
drwxr-xr-x 1 root   root     42 Sep 20 20:53 ..
drwxr-xr-x 1 nobody nogroup  90 Sep 21 10:25 site1
drwxr-sr-x 1 nobody nogroup 528 Mar 18 03:33 site2

As you can see the root /mnt/data/ct1 was indeed shifted to the container’s root, however the webdata folder and everything bellow it (that usually is the www-data in other containers) shows up as nobody:nogroup.

Thank you.

I manage to solve it using raw.idmap.

Let’s say you’ve a web container called web1. First you mount the folder where your web root lives to the pureftpd container. Don’t shift it:

incus config device add pureftpd web disk source=/data/web/web1 path=/web/web1 shift=false

web1 config shows volatile.idmap.base: "1196608" and that means:

  • Files owned by root user inside will show up on the host as owned by 1196608
  • Files owned by www-data:www-data inside will show up on the host as 1196709:1196710

You could give your pureftpd container mappings access to those IDs:

incus config set pureftpd raw.idmap "uid 1196709 1196709
gid 1196710 1196710"

Now repeat the process for every container you may have.

That would make every UID/GID used by the web container readable and writable from the pureftpdcontainer.

If you want to automate it: write a bash script that takes the web container, mounts the web root and then looks up your /data/web/web1/public directory with stat to find out the UID/GID and add it to the pureftpd container raw.idmap automatically for you. Here’s a starting point:

USERNAME="web1" # this is the web container name, also the "username" for the ftp
CT_PUREFTPD="pureftpd"
STORAGE_DIR_SOURCE="/data/$USERNAME"
STORAGE_DIR_CT="/web/$USERNAME"

# Get the "public" dir ids
if [ ! -d "$STORAGE_DIR_SOURCE/public" ]; then
  echo "Error: $STORAGE_DIR_SOURCE/public does not exist."
  exit 1
fi
WEB_UID=$(stat -c %u "$STORAGE_DIR_SOURCE/public")
WEB_GID=$(stat -c %g "$STORAGE_DIR_SOURCE/public")
echo "IDs for $STORAGE_DIR_SOURCE are $WEB_UID $WEB_GID"

# Get existing raw.idmap values
raw_map=$(incus config get "$CT_PUREFTPD" raw.idmap)

# Check if UID exists
if ! echo "$raw_map" | grep -q "uid $WEB_UID"; then
    echo "Adding UID mapping $WEB_UID"
    incus config set $CT_PUREFTPD raw.idmap "$raw_map"$'\n'"uid $WEB_UID $WEB_UID"
else
    echo "UID $WEB_UID already exists."
fi

# Check if GID exists
if ! echo "$raw_map" | grep -q "gid $WEB_GID"; then
    echo "Adding GID mapping $WEB_GID"
    # Fetch latest map again (in case UID was added)
    raw_map=$(incus config get "$CT_PUREFTPD" raw.idmap)
    incus config set $CT_PUREFTPD raw.idmap "$raw_map"$'\n'"gid $WEB_GID $WEB_GID"
else
    echo "GID $WEB_GID already exists."
fi

echo "Mounting $STORAGE_DIR_SOURCE to $CT_PUREFTPD"
incus config device add $CT_PUREFTPD $USERNAME disk source=$STORAGE_DIR_SOURCE path=/web/$USERNAME shift=false

# Go have fun creating the account from the $USERNAME on pureftpd as well using pure-pw useradd :)