Solutions below use namespaces through the unshare command. Processes in other namespaces will not see the mount.
We can't use unshare -r because it's run with nogroups group rights, that encfs/fuse don't like.
Solution 1 (will ask for root rights at every launch)
We're using sudo unshare to create the bind mount and mark it as private. Once done, we change the user to sudo caller (sudo -u "#$SUDO_UID" -g "#$SUDO_GID") end explicitely remove root rights (sudo -K). At that point, we can call encfs and start to do stuff with your_program_here. When terminated, the private bind mount is deleted by the parent root bash (so without asking again for the user root password) so it is no more visible by root through mount -l.
enc_dir="/path/to/enc_dir"
mount_point="/path/to/plain_dir"
RUN_CMD="mount --bind \"$mount_point\" \"$mount_point\"; \
mount --make-private \"$mount_point\"; \
sudo -u \"#\$SUDO_UID\" -g \"#\$SUDO_GID\" \
bash -c \"sudo -K; \
encfs \\\"$enc_dir\\\" \\\"$mount_point\\\"; \
your_program_here \\\"\\\$@\\\"; \
fusermount -u -z \\\"$mount_point\\\" \
\" - \"\$@\"; \
umount \"$mount_point\""
sudo unshare -m bash -c "$RUN_CMD" - "optional" "arguments"
your_program_here can be whatever you want, e.g. bash for a command prompt. You can pass arguments to this program ($1="optional" $2="arguments") properly.
Source: Launch a sudoed command upon script termination? answer by Celada + see sources solution 2.
Solution 2 (has critical vulnerability if using unshare >= v2.27.1)
To avoid unshare being run as root, we need to use the setuid bit on it.
sudo groupadd unshare
sudo adduser $USER unshare
sudo chmod u+s /usr/bin/unshare
sudo chown root:unshare /usr/bin/unshare
Warning: This provokes a vulnerability in recent unshare versions (since v2.27.1) and must not be used, as unshare no longer drops root rights when executing the command, so anyone could have root access. A workaround exist and is described here.
Then (possibly a new terminal is required):
unshare -m
We should now have a shell with $ user sign, not # root sign. Now, we need to explicitly specify private or rprivate in the mount options (explaination in sources below) and then launch encfs.
enc_dir="/path/to/enc_dir"
mount_point="/path/to/plain_dir"
sudo mount --bind "$mount_point" "$mount_point"
sudo mount --make-private "$mount_point"
encfs "$enc_dir" "$mount_point"
Processes started from this shell will be able to view the mount point. Other processes won't.
Source: Hiding an encrypted folder in a shared system without root access (unshare -m) commented by Philipp Wendler & Cant' get per process mount namespaces to work answer by ub1quit33
Solution 3 ? (best of breed)
Avenues to be further explored: There's probably something to do by putting Solution 1 script in a .sh file, and setting setuid bit + chown on it like in Solution 2. #$SUDO_*ID variables may need to be replaced by something else and sudo -K effectiveness needs to be tested.
Also, enc_dir and mount_point variables should not be parameterizable as they are not used safely. Otherwise, they should be escaped, or better, they should be passed as unshare first parameters (hence $@ should be replaced by the range of positional parameters ${@:2}).
Or fill a feature request for encfs/fuse support in unshare -r to unshare developers...