We’ve all been there. You hire a freelance developer to patch a legacy PHP app, or an external data scientist needs to upload CSVs to your server. They ask for SSH access. Your heart drops. You know perfectly well that if you just create a standard user account, they can roam around your server. Even as an unprivileged user, they can peek into /etc, read system configurations, list running processes, and browse the /home directories of your engineering team.
In the world of Linux Security and System Administration, the principle of least privilege is law. You want to know exactly how to give a user permission to one folder linux without exposing your entire file system. You don’t want them poking around your Python scripts, your Nginx configs, or your PostgreSQL Linux database credentials.
I’ve spent over a decade doing Linux Administration, managing everything from bare-metal Debian Linux boxes to complex Kubernetes Linux clusters in AWS and Azure. I’ve seen junior admins make the catastrophic mistake of granting excessive permissions—or worse, using chmod 777—just to get a contractor off their back. Today, I’m going to show you the right way to lock a user down to a single directory. We will cover soft restrictions using Access Control Lists (ACLs), and hard restrictions using SSH Chroot Jails.
Why Basic File Permissions Aren’t Enough
Before we build our jail, we need to understand why standard Linux Permissions fail us in this specific scenario. The Linux File System is designed to be open by default for reading. If you create a new user on Ubuntu, CentOS, or Fedora Linux, that user gets a home directory. But nothing stops them from typing cd / and running the ls command.
Sure, they can’t modify files in /etc or /var without root access, but they can read a lot of them. They can read /etc/passwd. If you have poorly secured backup scripts or AWS Linux credentials sitting in another user’s directory, a snooping contractor can read those too. Even if you use strict File Permissions, the default Linux environment allows horizontal visibility.
If you want to isolate a user completely, you have two main approaches depending on your exact requirements:
- The ACL Approach: Good if you trust the user slightly, but just want to restrict their write/read access to a specific web directory (like an Apache or Nginx document root).
- The Chroot Jail Approach: The bulletproof method. The user logs in, and the system literally tricks them into thinking the target folder is the root
/of the entire hard drive.
Method 1: Using ACLs for “Soft” Directory Confinement
Let’s say you have a web application located at /var/www/html/project-x. You want your user, dev_contractor, to be able to read and write to this directory, but nowhere else. Standard chown and chmod commands get messy here, especially if your Linux Web Server (like Apache or Nginx) also needs ownership of those files.
This is where Access Control Lists (ACLs) come in. ACLs allow you to apply granular Linux Permissions to specific users without changing the base owner or group of the file.
Step 1: Install ACL Utilities
Most modern Linux Distributions (Ubuntu, Red Hat Linux, Arch Linux) support ACLs out of the box, but you might need to install the tools.
# On Debian/Ubuntu Tutorial systems:
sudo apt-get update
sudo apt-get install acl
# On Red Hat/CentOS/Fedora systems:
sudo dnf install acl
Step 2: Create the User and Restrict Their Home
First, create the user without a standard bash shell if they don’t need to execute scripts, or give them a restricted shell. We will also lock down their home directory.
sudo useradd -m -s /bin/bash dev_contractor
sudo passwd dev_contractor
sudo chmod 700 /home/dev_contractor
Step 3: Grant Access to the Target Folder
Now, we use setfacl. We want to give dev_contractor read, write, and execute permissions on /var/www/html/project-x. Note: the “execute” bit on a directory in Linux means the ability to “enter” or cd into it.
sudo setfacl -R -m u:dev_contractor:rwx /var/www/html/project-x
sudo setfacl -R -d -m u:dev_contractor:rwx /var/www/html/project-x
Pro-tip from the Linux Terminal: The -d flag sets the default ACL. This means any new files created in this directory in the future will automatically inherit these permissions. This saves you from getting paged at 3 AM because the contractor uploaded a new PHP file and the web server can’t read it.
Step 4: The Path Execution Problem
Here is where many sysadmins get stuck. If you tell someone how to give a user permission to one folder linux using ACLs, you must remind them about the parent directories. For dev_contractor to cd into /var/www/html/project-x, they must have execute (traverse) permissions on /var, /var/www, and /var/www/html.
sudo setfacl -m u:dev_contractor:x /var
sudo setfacl -m u:dev_contractor:x /var/www
sudo setfacl -m u:dev_contractor:x /var/www/html
This method works great for collaborative environments. However, dev_contractor can still run cd /tmp or look around the system. If you want absolute containment, we must build a Chroot Jail.

Method 2: The Bulletproof SSH Chroot Jail (SFTP Only)
If the user only needs to upload and download files (which is true 90% of the time for web developers), the most secure method is an SFTP-only Chroot Jail. A “chroot” (change root) operation changes the apparent root directory for the current running process. When they log in via Linux SSH, they are trapped. They cannot go up a level because, to them, their directory is /.
Step 1: Create the Target Directory and User
Let’s create a directory specifically for this purpose. We’ll call it /sftp_jail/contractor_data.
sudo mkdir -p /sftp_jail/contractor_data
sudo useradd -M -d /sftp_jail/contractor_data -s /usr/sbin/nologin jail_user
sudo passwd jail_user
Notice we used -s /usr/sbin/nologin. We are explicitly denying them Linux Terminal access. They cannot SSH in and get a bash prompt. They can only use SFTP.
Step 2: Configure Directory Permissions (Crucial Step)
Chroot jails have extremely strict ownership requirements. The OpenSSH daemon will flat-out refuse to let the user log in if the permissions are wrong. The directory that acts as the jail (the chroot root) must be owned by root and must not be writable by any other user or group.
# The jail root MUST be owned by root:root
sudo chown root:root /sftp_jail
sudo chmod 755 /sftp_jail
# Create a sub-directory where the user can actually write files
sudo mkdir /sftp_jail/contractor_data/uploads
sudo chown jail_user:jail_user /sftp_jail/contractor_data/uploads
sudo chmod 700 /sftp_jail/contractor_data/uploads
Step 3: Modify the SSH Daemon Configuration
Now we need to tell our Linux Server to trap this user upon login. Open your SSH configuration file using your favorite Vim Editor or Nano.
sudo vim /etc/ssh/sshd_config
Scroll to the very bottom of the file (Match blocks must always go at the end) and add the following lines:
Match User jail_user
ChrootDirectory /sftp_jail/contractor_data
ForceCommand internal-sftp
AllowTcpForwarding no
X11Forwarding no
PasswordAuthentication yes
Let’s break down these Linux Commands and directives:
- Match User: Applies these rules only to
jail_user. - ChrootDirectory: Sets their root
/to this specific path. - ForceCommand internal-sftp: Forces the SSH server to use its built-in SFTP server, overriding any shell requests.
- AllowTcpForwarding no: Prevents the user from using SSH tunneling to bypass firewalls or access internal Linux Databases like MySQL Linux.
Step 4: Restart SSH and Test
Before restarting SSH, always validate your configuration to avoid locking yourself out of your own Linux Server!
sudo sshd -t
sudo systemctl restart sshd
Now, test the connection from another terminal or use an SFTP client like FileZilla:
sftp jail_user@your_server_ip
If you run ls, you will only see the uploads folder. If you run cd .., nothing happens. You are successfully trapped! This is the definitive answer to how to give a user permission to one folder linux securely.
Method 3: The Interactive Shell Chroot Jail (Advanced)
Sometimes, an SFTP-only connection isn’t enough. What if the user needs to execute a Python Scripting task, compile C Programming Linux code with GCC, or run Bash Scripting tools inside that directory? Giving them an interactive shell inside a chroot jail is significantly harder because a chroot is entirely empty. It has no /bin/bash, no /bin/ls, and none of the shared libraries required to run those commands.
We have to manually copy the required binaries and their dependencies into the jail.
Step 1: Prepare the Jail Environment
Let’s set up a jail at /var/jail.
sudo mkdir -p /var/jail/{dev,etc,lib,lib64,usr,bin}
sudo mkdir -p /var/jail/usr/bin
sudo chown root:root /var/jail
Step 2: Copy Binaries and Dependencies
If we want the user to be able to use bash and ls, we need to copy them into the jail’s /bin directory. But Linux Tools rely on shared C libraries. We can find these dependencies using the ldd command.
ldd /bin/bash
This will output something like:
linux-vdso.so.1 (0x00007ffc8e7e1000)
libtinfo.so.6 => /lib/x86_64-linux-gnu/libtinfo.so.6 (0x00007f3e8c1a0000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f3e8bf78000)
/lib64/ld-linux-x86-64.so.2 (0x00007f3e8c33f000)
You must copy every single one of those files into the exact same directory structure within your jail. Because doing this manually is a nightmare, I wrote a quick Shell Scripting utility you can use to automate this. Create a file called jail_builder.sh:
#!/bin/bash
# A simple script to copy a binary and its dependencies into a chroot jail
JAIL="/var/jail"
APPS="/bin/bash /bin/ls /bin/cat /usr/bin/vim"
for app in $APPS; do
# Copy the binary
sudo cp -v --parents "$app" "$JAIL"
# Copy the dependencies
ldd "$app" | awk '{ print $3 }' | grep '^/' | xargs -I '{}' sudo cp -v --parents '{}' "$JAIL"
# Handle the special ld-linux linker
ldd "$app" | grep 'ld-linux' | awk '{ print $1 }' | xargs -I '{}' sudo cp -v --parents '{}' "$JAIL"
done
Run the script. It will populate your jail with bash, ls, cat, and the vim editor.
Step 3: Configure SSH for the Shell Jail
Update your /etc/ssh/sshd_config file just like we did for SFTP, but this time, allow terminal access.
Match User shell_prisoner
ChrootDirectory /var/jail
AllowTcpForwarding no
X11Forwarding no
Restart SSH. When shell_prisoner logs in, they will drop into a bash prompt, but they will be completely confined to /var/jail. They won’t have access to system monitoring tools like htop or the top command because the /proc filesystem isn’t mounted in their jail. This is excellent for Linux Automation environments where you want to isolate a specific Python System Admin script.
Method 4: The Bind Mount Trick (For Existing Directories)
There is a massive caveat to the SSH Chroot method: The ChrootDirectory must be owned by root. But what if the folder you want to share is /var/www/html/app, and that folder MUST be owned by the www-data user for your web server to function? SSH will refuse to chroot into a directory owned by www-data.
The solution is a Linux Administration technique called a “Bind Mount.” A bind mount takes an existing directory tree and replicates it under a different point.
First, set up your standard SFTP jail as described in Method 2, with strict root ownership:
sudo mkdir -p /sftp_jail/bind_user
sudo chown root:root /sftp_jail/bind_user
Next, create a mount point inside the jail:
sudo mkdir /sftp_jail/bind_user/app_files
Now, bind mount the real web directory into the jail directory:
sudo mount --bind /var/www/html/app /sftp_jail/bind_user/app_files
To make this persist across reboots, add it to your Linux Disk Management file, /etc/fstab:
/var/www/html/app /sftp_jail/bind_user/app_files none bind 0 0
Now, your user is chrooted into /sftp_jail/bind_user (which is safely owned by root), but they can seamlessly edit the files inside the app_files folder, which maps directly to your live web directory. This is the ultimate, production-grade answer to how to give a user permission to one folder linux without breaking your application’s existing permissions.
DevOps Approach: Automating Jails with Ansible
If you are managing dozens of servers, doing this by hand via the Linux Terminal is inefficient. In modern Linux DevOps environments, we use tools like Ansible to enforce infrastructure as code. Here is a quick Ansible playbook snippet to automate the SFTP jail creation. This ensures consistency across your AWS Linux or Container Linux fleet.
---
- name: Setup SFTP Jail for Contractor
hosts: webservers
become: yes
tasks:
- name: Create sftp-only group
group:
name: sftponly
state: present
- name: Create the restricted user
user:
name: contractor
group: sftponly
shell: /usr/sbin/nologin
home: /var/sftp/contractor
- name: Ensure jail root is owned by root
file:
path: /var/sftp/contractor
state: directory
owner: root
group: root
mode: '0755'
- name: Create writable upload directory
file:
path: /var/sftp/contractor/uploads
state: directory
owner: contractor
group: sftponly
mode: '0700'
- name: Configure sshd_config
blockinfile:
path: /etc/ssh/sshd_config
block: |
Match Group sftponly
ChrootDirectory %h
ForceCommand internal-sftp
AllowTcpForwarding no
X11Forwarding no
notify: Restart SSH
Troubleshooting Common Pitfalls
Setting up jails and strict file permissions rarely works perfectly on the first try. If you are tearing your hair out, check these common culprits:




