It was a quiet Saturday morning. I was trying to connect to my Raspberry Pi 5 (srv01) using Cursor's Remote SSH. But instead of a warm terminal welcome, I was met with a cold, hard failure.
The Cursor Remote-SSH connection. It wouldn't start. No heartbeat. Just a "404" error in the logs that seemed to point to a missing server artifact.
The internet pointed a finger at a common culprit: Architecture Mismatch. The forum threads were buzzing with tales of 32-bit userlands on 64-bit kernels causing a 404 Not Found when Cursor tried to fetch the server artifact (cli-linux-armhf.tar.gz).
I performed an autopsy on the remote system:
# Checking the architecture and bitness
uname -m && getconf LONG_BIT
# Output: aarch64, 64The victim was 64-bit through and through. The 404 was a ghost, a red herring. The server was already there, lying in wait at ~/.cursor-server/bin/linux-arm64/.
I dug deeper into the extension logs (~/.config/Cursor/logs/...). That's where I found the true blood splatter:
2026-03-28 05:52:57.046 [info] (ssh_tunnel) stderr: channel 3: open failed: administratively prohibited: open failed
2026-03-28 05:52:57.047 [error] [forwarding][code] error while creating socks forwarding Socket closed
"Administratively prohibited." This wasn't a missing file; it was a lockout. The system itself was slamming the door shut.
I started searching the server's configuration files. I looked in /etc/ssh/sshd_config, but it looked clean. Then I saw it—a small, unassuming file in /etc/ssh/sshd_config.d/ named hardening.conf.
Inside, the killer was hiding in plain sight:
# Disable unnecessary features
X11Forwarding no
AllowAgentForwarding no
AllowTcpForwarding no <-- THE MURDERER
AllowTcpForwarding no had strangled the SSH tunnel before it could even take its first breath. Cursor (and VS Code) requires TCP forwarding to create the SOCKS tunnel it uses for communication.
The fix was surgical. I flipped the bit and restarted the service:
# The intervention
sudo sed -i 's/AllowTcpForwarding no/AllowTcpForwarding yes/' /etc/ssh/sshd_config.d/hardening.conf
sudo systemctl restart sshThe tunnel cleared. The connection was established. Justice—and productivity—was served.
Just when I thought the case was closed, a new ghost appeared. The terminal wouldn't start. A persistent error haunted the UI:
"Terminal sandbox could not start. This may be caused by an AppArmor configuration on your Linux system (kernel 6.2+)."
Even in the SSH environment, the error persisted. It was as if the lockdown had followed me across the network.
I checked the local machine (Ubuntu 24.10, kernel 6.11). The evidence was in the dmesg:
audit: apparmor="DENIED" operation="capable" profile="unprivileged_userns" comm="bwrap" capability=8 capname="setpcap"
The culprit was a new security feature in modern kernels (6.2+) that restricts unprivileged user namespaces. Even if AppArmor profiles are present, they are often not applied correctly to Cursor's AppImage or binary helpers, leading to a fallback to a restricted unprivileged_userns profile.
The fix required a local override of the kernel's AppArmor restrictions. The user had the settings ready in /etc/sysctl.d/50-cursor.conf, but they were commented out—likely by a cautious admin or a botched update.
I applied the antidote:
# Enabling unprivileged user namespaces in AppArmor
sudo sysctl -w kernel.apparmor_restrict_unprivileged_userns=0
# Making it permanent
sudo sed -i 's/#kernel.apparmor_restrict_unprivileged_userns=0/kernel.apparmor_restrict_unprivileged_userns=0/' /etc/sysctl.d/50-cursor.conf
sudo sysctl -p /etc/sysctl.d/50-cursor.confThe sandbox opened. The terminal breathed. The SSH environment was finally free of the shadows of the local AppArmor policies.
- Local Issues Shadow Remote ones: Even when using SSH, Cursor's UI and its terminal-bridge processes run locally and are subject to local kernel security policies.
- AppArmor 6.2+ is Strict: Newer kernels have very aggressive defaults for user namespaces. If an application isn't explicitly profiled, it's effectively jailed.
- Check your
/etc/sysctl.d/: Always look for fragments that were meant to solve the problem but were left commented out.