How to Restrict Linux Users to Their Own Files in Multi-Tenant Shell Environments


1 views

In shared hosting setups where multiple customers (~100) have shell access to a single server, a critical security concern emerges. Many web applications and tutorials recommend using chmod 0777 permissions, which creates world-readable/writable files. This presents a significant vulnerability where one user could potentially access another user's files.

The most effective approach is to implement filesystem restrictions at the kernel level:


# Install required packages (Debian/Ubuntu)
sudo apt-get install libpam-mount libpam-chroot

# Configure /etc/security/chroot.conf
echo "username /home/username" | sudo tee -a /etc/security/chroot.conf

For more granular control, Linux namespaces provide isolation:


# Create a new namespace for the user
sudo unshare --mount --user --map-root-user --pid --fork

# Verify isolation
mount --bind /home/user1 /home/user1
mount | grep user1  # Should only show user1's mounts

Combine kernel restrictions with filesystem ACLs:


# Set default ACLs for user directories
setfacl -R -m u:username:rwx /home/username
setfacl -dR -m u:username:rwx /home/username
setfacl -R -m o::- /home/username

Configure Pluggable Authentication Modules (PAM) to enforce restrictions:


# /etc/pam.d/sshd
session required pam_namespace.so unmnt_remnt no_unmount_on_close
session required pam_mount.so
session required pam_chroot.so

Automate permission checks with a cron job:


#!/bin/bash
for user in /home/*; do
    find $user -perm -007 -exec chmod o-rwx {} \;
    find $user ! -user $(basename $user) -exec chown $(basename $user):$(basename $user) {} \;
done

In shared hosting environments with multiple shell users, security risks emerge when applications recommend overly permissive file permissions like 0777. While these permissions make filesystem operations easier for poorly written web applications, they expose all user data to every other user on the system through simple commands like cat /home/*/public_html/config.php.

The most robust approach uses Linux namespaces to create isolated filesystem views:

# Create private mount namespace
unshare --mount --map-root-user

# Mount tmpfs over /home to hide other users' files
mount -t tmpfs none /home

# Bind-mount only the current user's home directory
mkdir -p /home/$(whoami)
mount --bind /original/home/$(whoami) /home/$(whoami)

For systems without namespace support, we can implement strict ACLs:

# First deny all access to /home
setfacl -R -b /home
setfacl -R -m default:other:--- /home

# Then grant access only to owner directories
for user in /home/*; do
  setfacl -R -m u:${user##*/}:rx /home/$user
  setfacl -R -m default:u:${user##*/}:rx /home/$user
done

For modern kernels (5.8+), we can intercept file access attempts:

# BPF program to filter file accesses
SEC("lsm/file_open")
int BPF_PROG(file_open, struct file *file)
{
    struct task_struct *task = (struct task_struct *)bpf_get_current_task();
    uid_t uid = (uid_t)BPF_CORE_READ(task, cred, uid.val);

    // Compare file owner vs process owner
    if (file->f_inode->i_uid.val != uid) {
        bpf_printk("Blocked access to %s by uid %d", file->f_path.dentry->d_name.name, uid);
        return -EACCES;
    }
    return 0;
}

In production environments, combine multiple techniques:

  • Use SELinux/AppArmor to restrict shell capabilities
  • Implement inotify monitoring for permission changes
  • Combine with chroot or containers for additional isolation

The key is implementing these controls at the kernel level before users can access the filesystem, rather than relying on userspace protections that can be bypassed.