You are not logged in.
<edit: See my updated solution below.>
Hi,
I'm setting up a Guest account on my computer and want to use overlayfs to overlay a writeable tmpfs on a read-only home directory (derived from a squashfs). Ultimately, I'd like to use pam_mount to mount and unmount these filesystems, so that any changes the guest user has made, as well as his data, are automatically wiped upon logout.
Without using pam_mount, this is my setup, which works fine:
mkdir /tmp/guest
mount -t tmpfs -o size=100M none /tmp/guest
mkdir /tmp/guest/upper /tmp/guest/lower /tmp/guest/work
mount -t squashfs -o loop /home/guest.squashfs /tmp/guest/lower
mount -t overlay overlay -o lowerdir=/tmp/guest/lower,upperdir=/tmp/guest/upper,workdir=/tmp/guest/work /home/guest
Unfortunately, overlayfs needs workdir to be on the same filesystem as upperdir. So if upper is going to be on a tmpfs, I need to mount it first, then create the workdir and upperdir within it, then do the rest of the mounting. Directory creation is no problem with pam_exec, but pam_mount would have to hold off on mounting the overlay until after the dirs are created. Alternatively, pam_mount can create the mountpoints itself, but it then can't create workdir.
Any ideas how to work around that?
Another question: I've thought about using unionfs instead of overlayfs, because it does not need a workdir. According to the wiki, it is in the kernel as well, but if I try to use it, mount complains about unknown filesystem type. Do I have to load its module manually, and if so, how is it called?
Last edited by lastchancetosee (2017-04-14 12:18:30)
My ship don't crash! She crashes, you crashed her!
Offline
OK, I figured it out.
Firstoff, I tried doing it with unionfs-fuse (which does not need a workdir), but ran into a whole hell of permission issues, so that was no fun.
The whole problem arises from wanting the overlayed data to be in a temporary file system that is removed when the user logs out, so that they do not leave any data behind for others to look at.
Stupid me: There is already such a filesystem. XDG_RUNTIME_DIR is created (at /run/user/<uid> by default) as a tmpfs by systemd and removed again after logout. Which means I can use pam_exec to create upperdir, lowerdir and workdir under XDG_RUNTIME_DIR, use pam_mount to mount the squashed home directory image to lowerdir and lowerdir and upperdir to the Guest account's home directory with overlayfs.
Works flawlessly.
For future reference (edit names, uids and paths as needed):
Add to the end of /etc/security/pam_mount.conf.xml before </pam_mount>:
<!-- pam_mount parameters: Volume-related -->
<volume user="guest" fstype="squashfs" path="/home/guest.squashfs" mountpoint="/run/user/1001/home-overlay/basedir" options="noatime" />
<volume user="guest" fstype="overlay" path="overlay" mountpoint="/home/guest" options="lowerdir=/run/user/1001/home-overlay/basedir,upperdir=/run/user/1001/home-overlay/overlaydir,workdir=/run/user/1001/home-overlay/workdir" />
<mkmountpoint enable="1" remove="true" />
</pam_mount>
Create script /usr/local/bin/prepare-home-overlay to create mountpoints:
#!/bin/bash
# check to see if XDG_RUNTIME_DIR is set up properly
if [[ -d $XDG_RUNTIME_DIR ]]
then
# create directories for overlayfs
mkdir -p $XDG_RUNTIME_DIR/home-overlay/overlaydir
mkdir $XDG_RUNTIME_DIR/home-overlay/basedir
mkdir -p $XDG_RUNTIME_DIR/home-overlay/workdir
# change ownership of dirs to PAM_USER and their group
chown -R $PAM_USER:$(id -g $PAM_USER) $XDG_RUNTIME_DIR/home-overlay
# other people should not be able to read these
chmod -R go-rwx $XDG_RUNTIME_DIR/home-overlay
exit 0
else
# if XDG_RUNTIME_DIR does not exist, the script was probably called before systemd set it up
>&2 echo "XDG runtime directory does not exist, this is a problem."
>&2 echo "Maybe this script was called too early in the pam stack."
exit 1
fi
As per the wiki, the call to pam_mount needs to be in a separate file to ensure proper unmounting. Create /etc/pam.d/guesthome:
#%PAM-1.0
auth optional pam_exec.so seteuid /usr/local/bin/prepare-home-overlay
auth optional pam_mount.so
password optional pam_exec.so seteuid /usr/local/bin/prepare-home/overlay
password optional pam_mount.so
session optional pam_exec.so seteuid /usr/local/bin/prepare-home-overlay
session optional pam_mount.so
and call it in /etc/pam.d/system-local-login, /etc/pam.d/system-lremote-login and potentially /etc/pam.d/<your display manager>:
#%PAM-1.0
auth include system-login
auth include guesthome
account include system-login
account include guesthome
password include system-login
password include guesthome
session include system-login
session include guesthome
Last edited by lastchancetosee (2017-04-13 19:28:02)
My ship don't crash! She crashes, you crashed her!
Offline
You don't even need pam_mount, because pam_exec exports PAM_TYPE which can be, among others, open_session and close_session. Thus we can mount and unmount via pam_exec.
Script called by pam_exec:
#!/bin/bash
# check if we're opening or closing the session
case $PAM_TYPE in
open_session)
# if XDG_RUNTIME_DIR is not set, we can't do anything
if [[ ! -d $XDG_RUNTIME_DIR ]]; then exit 12; fi
# create directories
mkdir -p $XDG_RUNTIME_DIR/guestacc/upper
mkdir $XDG_RUNTIME_DIR/guestacc/lower
mkdir $XDG_RUNTIME_DIR/guestacc/work
mkdir $XDG_RUNTIME_DIR/guestacc/merged
# set ownership/permissions
chown -R $PAM_USER:$(id -g $PAM_USER) $XDG_RUNTIME_DIR/guestacc
chmod -R go-rwx $XDG_RUNTIME_DIR/guestacc
# mount the account image and the overlay
mount -t squashfs -o loop,noatime /home/$PAM_USER.squashfs $XDG_RUNTIME_DIR/guestacc/lower
mount -t overlay overlay -o lowerdir=$XDG_RUNTIME_DIR/guestacc/lower,upperdir=$XDG_RUNTIME_DIR/guestacc/upper,workdir=$XDG_RUNTIME_DIR/guestacc/work $XDG_RUNTIME_DIR/guestacc/merged
exit 0
;;
close_session)
# only unmount/remove stuff if it was mounted/present
if mountpoint -q $XDG_RUNTIME_DIR/guestacc/merged; then umount $XDG_RUNTIME_DIR/guestacc/merged; fi
if mountpoint -q $XDG_RUNTIME_DIR/guestacc/lower; then umount $XDG_RUNTIME_DIR/guestacc/lower; fi
if [[ -d $XDG_RUNTIME_DIR/guestacc ]]; then rm -r $XDG_RUNTIME_DIR/guestacc; fi
exit 0
;;
*)
# not in session stack, do nothing but don't fail
exit 0
;;
esac
Put this in /etc/pam.d/<filename called in system-local-login etc.> to make the script only run when a guest user logs in:
session [default=1 success=ignore] pam_succeed_if.so quiet user ingroup guestacc
session optional pam_exec seteuid /path/to/script
Add guest user to group 'guestacc', set their home directory to /run/user/<their uid>/guestacc/merged
My ship don't crash! She crashes, you crashed her!
Offline