Directory remapping between users or processes on Linux? - linux

For example, I want to redirect the directory /data between users.
When user1 access /data, it accesses /data1 actually.
When user2 access /data, he accesses /data2 actually.
What technology should I use? cgroups? unionfs? others? I'm sorry I'm a newbie.
More advanced, redirection between processes.
process1 accesses /data1 as /data ,
process2 accesses /data2 as /data .
How can I do that?

There are Linux filesystem namespaces that can do what you want. You would create a new namespace and mount /data inside it as a bind mount to the real /data1 or /data2.
However, this is kind of tricky to do right now, as far as I know, and needs a lot of tooling that most Linux distros may not be using.
Most Unix software uses environment variables to find their data directories. In something like this, you'd have
export JACKSPROGRAMDATA=/data1
in the user's $HOME/.profile (or .bash_profile), and jacksprogram would use getenv(JACKSPROGRAMDATA) to read the value.

In Linux, you can use bind mounts to map directory or file to another path, and per-process mount namespaces to do it for specific process.
Bind mounts are implemented in -o bind option of mount. Mount namespace can be employed e.g. using unshare tool which is part of util-linux package.
See examples in this answer.

Mount namespaces allow to setup a different view of the filesystem private to all processes run within that namespace. You can then use mount --bind within that namespace to map directories.
For example, on user login you can create a namespace dedicated to that user. Within that namespace, you can use mount --bind to mount the directory /opt/data/$USER on top of data. You can then run the user shell in that namespace. For that shell and any other process started within that shell, any read or write in /data/ will end up reading and writing from /opt/data/$USER instead.
To automate the setup, you can use the pam_namespace pam module. A configuration file /etc/security/namespace.conf similar to this:
/data /opt/data/$USER level root,adm
could be all you need to make this work.
Alternatively, you could use an utility like faketree to do this interactively from the shell or in your CI/CD pipelines:
faketree --mount /opt/data/$USER:/data -- /bin/bash
(does not require root, uses namespaces)
You can read more about faketree in the main repository for the tool or in this blog post.

Related

Normal user touching a file in /var/run failed

I have a program called HelloWorld belonging to user test
HelloWorld will create a file HelloWorld.pid in /var/run to keep single instance.
I using following command to try to make test can access /var/run
usermod -a -G root test
However, when I run it, falied
could someone help me?
What are the permissions on /var/run? On my system, /var/run is rwxr-xr-x, which means only the user root can write to it. The permissions do not allow write access by members of the root group.
The normal way of handling this is by creating a subdirectory of /var/run that is owned by the user under which you'll be running your service. E.g.,
sudo mkdir /var/run/helloworld
sudo chown myusername /var/run/helloworld
Note that /var/run is often an ephemeral filesystem that disappears when your system reboots. If you would like your target directory to be created automatically when the system boots you can do that using the systemd tmpfiles service.
Some linux systems store per-user runtime files in /var/run/user/UID/.
In this case you can create your pid file in /var/run/user/$(id -u test)/HelloWorld.pid.
Alternatively just use /tmp.
You may want to use the user's name as a prefix to the pid filename to avoid collision with other users, for instance /tmp/test-HelloWorld.pid.

Why does mount --bind in linux require root privileges?

Even if you have all permissions for mount source/destination it requires root, why?
Alternatively, you can use bindfs to do the same thing as with mount --bind but with the added bonus that bindfs won't require you root privileges, because it is a FUSE filesystem.
That's simply because the underlying system call (see 'man 2 mount') requires CAP_SYS_ADMIN capability no matter what the mount options are.
Allowing a user to perform mount calls would probably be rather tricky regarding security.
If you want to allow certain mount operations you can use a tool like sudo by writing appropriate rules for it.

How to run a command in a chroot jail not as root and without sudo?

I'm setting up a minimal chroot and want to avoid having sudo or su in it but still run my processes as non-root. This is a bit of a trick as running chroot requiers root. I could write a program that does this that would look something like:
uid = LookupUser(args[username]) // no /etc/passwd in jail
chroot(args[newroot])
cd("/")
setuids(uid)
execve(args[exe:])
Is that my best bet or is there a standard tool that does that for me?
I rolled my own here:
If you invoke chroot from root, the chroot option --userspec=USER:GROUP will run the command under the non-root UID/GID.
By the way, the option '--userspec' is first introduced in coreutils-7.5 according to a git repository git://git.sv.gnu.org/coreutils.
fakechroot, in combination with fakeroot, will allow you to do this. They'll make all programs that are running act as if they're being run in a chroot as root but they'll actually be running as you.
See also fakechroot's man page.
You can make use of linux capabilities to give your binary the ability to call chroot() w/o being root. As an example, you can do this to the chroot binary. As non-root, normally you'd get this:
$ chroot /tmp/
chroot: cannot change root directory to /tmp/: Operation not permitted
But after you run the setcap command:
sudo setcap cap_sys_chroot+ep /usr/sbin/chroot
It will let you do the chroot call.
I don't recommend you do this to the system's chroot, that you instead do it to your own program and call chroot. That way you have more control over what is happening, and you can even drop the cap_sys_chroot privilege after you call it, so successive calls to chroot in your program will fail.
A custom chrooter isn't at all hard to write:
#define _BSD_SOURCE
#include <stdio.h>
#include <unistd.h>
const char newroot[]="/path/to/chroot";
int main(int c, char **v, char **e) {
int rc; const char *m;
if ( (m="chdir" ,rc=chdir(newroot)) == 0
&& (m="chroot",rc=chroot(newroot)) == 0
&& (m="setuid",rc=setuid(getuid())) == 0 )
m="execve", execve(v[1],v+2,e);
perror(m);
return 1;
}
Make that setuid root and owned by a custom group you add your favored user to (and no 'other' access).
You could use Linux Containers to create a chroot environment that is in a totally different namespace (IPC, filesytem, and even network)
There is even LXD which is able to manage the creation of image-based containers and configure them to run as unprivileged users so that if the untrusted code manages to somehow escape the container, it will only be able to execute code as the unprivileged user and not as the system's root.
Search 'Linux Containers' and 'LXD' on your favorite search engine ;)
These days chrooting without root-permissions is possible with unshare command provided by mount namespaces.
Plain Unshare
Suppose you want to chroot into ~/Projects/my-backup directory, and run inside it the ~/Projects/my-backup/bin/bash binary . So you run:
$ unshare -mr chroot ~/Projects/my-backup/ /bin/bash
Here:
-m means you can use mount --bind inside the new chroot (note that mounts will not be visible to the outside world, only to your chroot)
-r makes it look as if you are root inside the chroot
chroot … is the usual chroot command.
Possible pitfalls:
Make sure you have environment variables set correctly. For example, upon chrooting into an Ubuntu from Archlinux I was getting errors like bash: ls: command not found. Turned out, my $PATH did not contain /bin/ (on Archlinux it's a symlink to /usr/bin/), so I had to run:
PATH=$PATH:/bin/ unshare -mr chroot ~/Projects/my-backup/ /bin/bash
Note that /proc or /dev filesystems won't get populated, so running a binary that requires them will fail. There is a --mount-proc option, but it doesn't seem to be accessible to a non-root user.
chown won't work unless you do some complicated setup with newuidmap and newgidmap commands.
Buildah Unshare
This has advantage compared to plain unshare in that it takes care of the needed setup for chown command to start working. You only need to make sure you have defined the ranges in /etc/subuid and /etc/subgid for your user.
You run it as:
$ buildah unshare chroot ~/Projects/my-backup/ /bin/bash

FreeBSD Jail and SSH - /dev/tty: No such file or directory

When I try to connect through SSH from inside the JAIL I get this error:
# ssh test#test.com
...
debug1: read_passphrase: can not open / dev / tty: No such file or directory
Host key verification failed.
Outside JAIL everythng is working properly. Any ideas?
Steps to reproduce:
# jls
JID IP Address Hostname Path
1 10.10.3.1 demo.example.com /jails/demo
# jexec 1 tcsh
(inside jail:)
# ssh test#test.com
Does your jail root have a populated /dev filesystem through a devfs mount? It looks like it doesn't right now.
Important note: You should be able to use devfs rules to limit the devices visible to jailed processes. In particular, access to raw disk device nodes is a bad idea. The jail(8) manpage describes this near the following paragraph:
It is important that only appropriate device nodes in devfs be exposed to a jail; access to disk devices in the jail may permit processes in the jail to bypass the jail sandboxing by modifying files outside of the jail. See devfs(8) for information on how to use devfs rules to limit access to entries in the per-jail devfs. A simple devfs ruleset for jails is available as ruleset #4 in /etc/defaults/devfs.rules.
You should be able to mount devfs under /jails/demo/dev and apply the recommended jail device rules by running as root the following commands:
# mkdir /jails/demo/dev
# mount -t devfs devfs /jails/demo/dev
# devfs -m /jails/demo/dev rule -s 4 applyset
Of course, you can also write a custom ruleset in /etc/defaults/devfs.rules, even a special devfs ruleset that only applies to a specific jail.
For more details see also the manpages for jail(8), devfs(8), and devfs.rules(5).
You may also experience this if you're entered the jail via the jail command. If you start up the jail and SSH into it, you should have better luck.
The devfs filesystem is probably not mounted in your jail. Many things will fail, not just ssh.
To mount a properly-filtered devfs automotically, your best bet is to use rc.conf variables:
jail_enable=YES
jail_list="JAILNAME"
jail_devfs_enable=YES
jail_JAILNAME_rootdir='/jails/demo'
jail_JAILNAME_hostname="demo"
Then you can stop/stop it using "/etc/rc.d/jail start demo", e

How to register FUSE filesystem type with mount(8) and fstab?

I've written a small FUSE-based filesystem and now the only part's missing is that I want to register it with fstab(5) to auto-mount it on system startup and/or manually mount it with just mount /srv/virtual-db. How can I achieve this?
I know, I can just run /usr/bin/vdbfs.py /srv/virtual-db from some init script, but that's not exactly pretty.
I'm sorry because this may be not exactly a programming question, but it's highly related, as the packaging and deployment is still the programmer's job.
In general, one "registers" a new mount filesystem type by creating an executable mount.fstype.
$ ln -s /usr/bin/vdbfs.py /usr/sbin/mount.vdbfs
If vdbfs.py takes mount-ish arguments (i.e. dev path [-o opts]), then mount -t vdbfs and using vdbfs as the 3rd field in fstab will work. If it doesn't, you can create a wrapper which does take arguments of that form and maps them to whatever your vdbfs.py takes.
FUSE should also install a mount.fuse executable; mount.fuse 'vdbfs.py#dev' path -o opts will go on and call vdbfs.py dev path -o opts. In that case, you can use fuse as your filesystem type and prefix your device with vdbfs.py#.
So to clarify ephemient's answer, there are two options:
Edit /etc/fstab like this:
# <file system> <mount point> <type> <options> <dump> <pass>
# ...
vdbfs.py#<dev> /srv/virtual-db fuse user,<other-opts> 0 0
Or,
Create an executable prefixed with "mount." (ensuring it can be used
with mount-like options):
$ ln -s /usr/bin/vdbfs.py /usr/sbin/mount.vdbfs
And edit /etc/fstab like this:
# <file system> <mount point> <type> <options> <dump> <pass>
# ...
<dev> /srv/virtual-db vdbfs.py user,<other-opts> 0 0
With regards to auto-mounting at start up and manually mounting with mount, the user and noauto options are relevant and fully supported by fuse itself so you don't have to implement them yourself. The user option lets a non-priveleged user who is a member of the "fuse" group mount your filesystem with the mount command, and noauto directs your filesystem not to automatically mount at startup. If you don't specify noauto, it will automatically mount.
To clarify #patryk.beza comment on the accepted answer, the correct way to mount a FUSE file system is by setting the file system type to fuse.<subtype>.
For example, to mount an s3fs-fuse implementation, which does not provide a specific /sbin/mount.* wrapper and uses normally the s3fs user command to mount S3 buckets, one can use this command as root:
mount -t fuse.s3fs bucket-name /path/to/dir -o <some,options>
or this line in /etc/fstab:
bucket-name /path/to/dir fuse.s3fs <some,options> 0 0
or this SystemD mount unit (for example, /etc/systemd/system/path-to-dir.mount):
[Unit]
Description=S3 Storage
After=network.target
[Mount]
What=bucket-name
Where=/path/to/dir
Type=fuse.s3fs
Options=<some,options>
[Install]
WantedBy=multi-user.target
How this works: mount recognizes the concept of "filesystem subtypes" when the type is formatted with a period (i.e. <type>.<subtype>), so that a type with the format fuse.someimpl is recognized to be the responsibility of the FUSE mount helper /sbin/mount.fuse. The FUSE mount helper then resolves the someimpl part to the FUSE implementation, in the same way as the # format is used in the original answer (I think this is just a path search for a program named <subtype>, but I'm not 100% sure about it).
You could just use fuse filesystem type. The following works on my system:
smbnetfs /media/netbios fuse defaults,allow_other 0 0
Another example:
sshfs#user#example.com:/ /mnt fuse user,noauto 0 0
After researching a lot found this solution to mount fuse filesystem suing fstab entry. I was using fuse for s3bucket to mount on local linux machine.
.passwd-s3fs : Is containing credentials to access your aws account 1] Secret key and 2] Access Key .
uid : User Id. You can type linux command id and you can get uid
Syntax:
s3fs#<Bucket_Name> <Mounted_Direcotry_Path> fuse _netdev,allow_other,passwd_file=/home/ubuntu/.passwd-s3fs,use_cache=/tmp,umask=002,uid=<User_Id> 0 0
Example:
s3fs#myawsbucket /home/ubuntu/s3bucket/mys3bucket fuse _netdev,allow_other,passwd_file=/home/ubuntu/.passwd-s3fs,use_cache=/tmp,umask=002,uid=1000 0 0
To mount you need run following command.
mount -a
To check your bucket is mounted properly or not use following command to check which shows all mounted points.
df -h

Resources