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?
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:
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.
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.
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 :)