Mounting a Local USB Drive to a Cloud VPS Securely with SSHFS and a Chroot Jail
Cloud VPS instances are great for compute, testing, and always-on development environments—until you start running low on disk space or want to work with large local files directly from your cloud workspace without uploa
Cloud VPS instances are great for compute, testing, and always-on development environments—until you start running low on disk space or want to work with large local files directly from your cloud workspace without uploading gigabytes over the internet every time.
Instead of paying for extra block storage, you can bridge an external USB drive plugged into your local Ubuntu-based workstation straight to any cloud VPS using a reverse SSH tunnel and SSHFS. Even better: with a few OpenSSH lockdown rules, you can ensure that even if your VPS were ever compromised, an attacker would be strictly trapped inside your USB drive with zero shell access, zero port forwarding, and zero visibility into your local home directory.
Here is a universal, step-by-step guide that works across Ubuntu (and Ubuntu-based desktops) and any Linux VPS provider, including seamless integration with VS Code Remote-SSH.
How the Architecture Works
Because your home computer sits behind a router/NAT, a cloud VPS cannot initiate a connection to it directly. We solve that cleanly without opening any ports on your home router:
-
Reverse SSH Tunnel: When you connect from your local machine to your VPS (
ssh my-vps), SSH opens a reverse tunnel on port2222of the VPS that points back to port22on your local machine. -
SSHFS Mount: Your VPS mounts your local USB drive over that encrypted
localhost:2222tunnel usingsshfs, making it appear as a normal folder (~/usb_storage). -
OpenSSH Chroot &
restrictJail: On your local machine, OpenSSH forces any connection coming through that key into an SFTP-only chroot jail locked to/media/<local-user>, blocking interactive shells, directory traversal (cd ..), and network port forwarding.
Step 1: Prepare Your Local Ubuntu Machine
Plug in your external USB drive. On most Ubuntu-based desktops, it automatically mounts under /media/<local-user>/<drive-label>. Verify the exact mount path and label by running:
lsblk -o NAME,LABEL,MOUNTPOINTS
Take note of your local username (e.g., alex) and your drive's folder name (e.g., my_usb_drive), which gives a full path like /media/alex/my_usb_drive.
Next, make sure the OpenSSH server is installed and running locally so the reverse tunnel has an endpoint to connect to:
sudo apt update && sudo apt install -y openssh-server
sudo systemctl enable --now ssh
Verify it is active:
systemctl is-active ssh
# Output: active
Step 2: Configure the Reverse Tunnel in ~/.ssh/config
On your local machine, configure a host alias for your VPS in ~/.ssh/config so that every time you connect via terminal or VS Code Remote-SSH, port 2222 is automatically forwarded:
Host my-vps
HostName YOUR_VPS_IP
User YOUR_VPS_USER
IdentityFile ~/.ssh/YOUR_VPS_KEY
IdentitiesOnly yes
RemoteForward 2222 localhost:22
(Replace YOUR_VPS_IP, YOUR_VPS_USER, and YOUR_VPS_KEY with your VPS details.)
From now on, running ssh my-vps (or connecting to my-vps in VS Code) establishes both your session and the reverse bridge automatically.
Step 3: Set Up Passwordless SSH Keys (VPS $\rightarrow$ Local)
For your VPS to mount the USB drive automatically without prompting for your local password every time, generate a dedicated SSH key pair on your VPS:
ssh-keygen -t ed25519 -N "" -f ~/.ssh/id_ed25519
cat ~/.ssh/id_ed25519.pub
Copy the single line starting with ssh-ed25519 .... We will add it to your local machine's ~/.ssh/authorized_keys file in the next step—along with strict security restrictions.
Step 4: Lock Down Access to Only Your USB Drive
Giving a cloud server SSH access back into your personal workstation is a serious security risk if left unrestricted. We lock this down in two layers on your local Ubuntu machine.
Layer 1: Restrict the Public Key in ~/.ssh/authorized_keys
Ensure your ~/.ssh directory exists with strict permissions:
mkdir -p ~/.ssh && chmod 700 ~/.ssh
Add your VPS's public key to ~/.ssh/authorized_keys on one single line, prepended with OpenSSH's restrict and command options (replacing <drive-label> with your USB drive's folder name):
restrict,command="internal-sftp -d /<drive-label>" ssh-ed25519 AAAA_YOUR_VPS_PUBLIC_KEY_HERE user@your-vps
Then lock down the file permissions:
chmod 600 ~/.ssh/authorized_keys
-
restrictdisables interactive shell access, port forwarding, X11 forwarding, and agent forwarding for this key. -
command="internal-sftp -d /<drive-label>"forces the session directly into SFTP inside your USB drive folder.
Layer 2: Enforce a Kernel-Level Chroot Jail in /etc/ssh/sshd_config
To guarantee that nobody using SFTP can traverse up into /home/<local-user> or / on your local filesystem, add a Match User block to the bottom of /etc/ssh/sshd_config on your local machine (replacing <local-user> with your local Ubuntu username):
sudo tee -a /etc/ssh/sshd_config > /dev/null << 'EOF'
Match User <local-user>
ForceCommand internal-sftp
ChrootDirectory /media/<local-user>
PermitTunnel no
AllowAgentForwarding no
AllowTcpForwarding no
X11Forwarding no
EOF
Why
/media/<local-user>? OpenSSH requires every directory in aChrootDirectorypath to be owned byroot:root. Since/media/<local-user>is owned byrootwhile your mounted USB drive/media/<local-user>/<drive-label>is owned by your user, setting the chroot root to/media/<local-user>satisfies OpenSSH's security requirements while keeping your drive writable. Inside SFTP,/media/<local-user>/<drive-label>simply appears as/<drive-label>.
Test the SSH configuration and reload the service:
sudo sshd -t && sudo systemctl reload ssh
Step 5: Install sshfs and Mount the Drive on Your VPS
Connect to your VPS (ssh my-vps), install sshfs, and create your mount point:
sudo apt update && sudo apt install -y sshfs
mkdir -p ~/usb_storage
Pro Tip: Protect Against Accidental Writes When Unmounted
Before mounting, set the empty ~/usb_storage directory on your VPS to read-only (chmod 555):
chmod 555 ~/usb_storage
When your USB drive is mounted on top of ~/usb_storage, your normal read/write permissions take over. If the drive ever disconnects or isn't mounted yet, chmod 555 ensures VS Code or scripts immediately throw a Permission denied error rather than silently writing gigabytes of files onto your VPS's internal SSD.
Now mount the USB drive over the reverse tunnel (replacing <local-user> and <drive-label>):
sshfs -p 2222 <local-user>@localhost:/<drive-label> ~/usb_storage -o reconnect,ServerAliveInterval=15
Verify that it mounted:
df -h ~/usb_storage && ls -la ~/usb_storage
You should see your full USB drive capacity and files (such as lost+found on an ext4 drive).
Step 6: Automate Mounting for Both Terminals and VS Code
1. Auto-Mount on SSH Login (~/.profile and ~/.bashrc)
Add a check to both ~/.profile and ~/.bashrc on your VPS so that whenever port 2222 is active and ~/usb_storage isn't already mounted, it connects automatically (replacing <local-user> and <drive-label>):
cat << 'EOF' | tee -a ~/.profile ~/.bashrc > /dev/null
# Auto-mount local USB drive when reverse tunnel (port 2222) is open
if ! mountpoint -q "$HOME/usb_storage" && ss -tln | grep -q ':2222 '; then
sshfs -p 2222 <local-user>@localhost:/<drive-label> "$HOME/usb_storage" -o reconnect,ServerAliveInterval=15 2>/dev/null
fi
EOF
2. Auto-Mount When Opening VS Code Remote-SSH
VS Code's Remote-SSH extension connects using a background server that can skip interactive shell startup scripts. To make sure opening your project in VS Code always mounts the drive automatically, create a .vscode/tasks.json file inside ~/usb_storage:
mkdir -p ~/usb_storage/.vscode
cat << 'EOF' > ~/usb_storage/.vscode/tasks.json
{
"version": "2.0.0",
"tasks": [
{
"label": "Mount USB Storage",
"type": "shell",
"command": "mountpoint -q ~/usb_storage || sshfs -p 2222 <local-user>@localhost:/<drive-label> ~/usb_storage -o reconnect,ServerAliveInterval=15",
"runOptions": {
"runOn": "folderOpen"
},
"presentation": {
"reveal": "silent"
}
}
]
}
EOF
When you open ~/usb_storage (or a project subfolder inside it) in VS Code, click Allow when prompted to permit automatic folder tasks. From then on, VS Code mounts the drive in the background every time you open your workspace.
(Tip: Open a specific project folder like ~/usb_storage/my-project rather than the root drive so VS Code's file watcher doesn't try to index your entire multi-terabyte drive over SSH.)
Step 7: Verify the Security Lockdown
From your VPS terminal, test that your local workstation is completely isolated:
- Test Shell Execution:
ssh -p 2222 <local-user>@localhost "ls -la /home/<local-user>"
Result: Immediately rejected with This service allows sftp connections only.
- Test Directory Traversal in SFTP:
sftp -P 2222 <local-user>@localhost << 'EOF'
pwd
cd ..
pwd
cd /home/<local-user>
EOF
Result: pwd stays locked at / (your /media/<local-user> chroot root), cd .. cannot escape /, and /home/<local-user> returns No such file.
- Test Port Forwarding:
ssh -f -N -L 9999:127.0.0.1:22 -p 2222 <local-user>@localhost && curl -v telnet://127.0.0.1:9999; pkill -f "9999:127.0.0.1"
Result: Blocked immediately (administratively prohibited), confirming the VPS cannot pivot into your local network.
Daily Workflow Summary
Once configured, your day-to-day workflow is effortless:
- Plug in your USB drive on your local Ubuntu machine.
-
Connect to your VPS via
ssh my-vpsor VS Code Remote Explorer—the tunnel opens and~/usb_storagemounts automatically. - Before unplugging the USB drive or shutting down, run a clean unmount from your VPS terminal so FUSE doesn't leave a stale mount behind:
fusermount3 -u ~/usb_storage
(If a terminal or editor still has the folder open, use fusermount3 -uz ~/usb_storage for a clean lazy unmount.)
Want more Linux, self-hosting, and developer workflow guides? Check out more posts at techray.dev.
Originally published by Dev.to Security. Aggregated on AIWithGhost for educational purposes — full credit and traffic to the original publisher.