我第一次接触 Restricted Shell 是在一个给外包人员开放服务器查询权限的需求里。当时的想法很朴素:给一个不能 cd、不能改 PATH 的 shell,他总该老实了吧。结果不到半天,对方就把脚本写到 /tmp 下直接跑了起来——当然这不怪他,怪我没读懂 Bash 手册里那句被无数人忽略的话:A restricted shell does not provide a security boundary。
后来我花了很长时间重新梳理这个功能,才明白一个道理:受限 Shell 不是用来防恶意的,它是用来防手滑的。它的价值在于降低误操作半径,而不是构建一个攻不破的牢笼。这篇文章就围绕 Bash 的 Restricted Shell 展开,讲清楚它到底限制了什么、怎么正确激活、哪些场景真正适合用它,以及最关键的——为什么你不能只靠它来保护一台机器。适合正在做 Linux 运维、或者想给临时账号加上操作边界的读者,也适合被"restricted shell 为什么一碰就碎"这个问题困扰过的人。
1. 什么场景下你会需要一把"锁住的终端"
1.1 受限 Shell 在真实运维中的位置
我在实践里发现,绝大多数人认识 restricted shell 都是出于类似的需求:给客户开一个只读查询账号、给外包团队一个只能执行指定命令的入口、或者给无人值守的 kiosk 机器准备一个无法乱跑的交互环境。
这些需求有个共同点:用户本身不是恶意的,他们只是可能不熟悉系统、可能会误操作、可能会在好奇心的驱使下敲出几个危险命令。受限 Shell 解决的就是这个问题——它像给剪刀加了一个儿童锁,不是让人撬不开,而是让正常使用过程中的意外大幅度减少。
Linux 世界里能做到类似事的工具不少,比如 rssh、scponly,甚至可以把用户的登录 shell 直接指向某个脚本。但 Bash 自带的 restricted mode 和这些方案不太一样:它不需要额外安装任何软件,只需要一个参数或者一个链接名就能激活,天然集成在每一份 Bash 里,所以非常适合作为第一道防线。
1.2 受限 Shell 和普通 Bash 的本质区别
很多人以为 restricted shell 是一套独立的程序,其实不是。它就是 Bash 本身,只是启动时带上了 -r 选项,或者通过名为 rbash 的符号链接被调用,从而进入受限模式。
进入受限模式后,Bash 依然执行启动文件(比如 /etc/profile、~/.bashrc 等),依然会读取环境变量,但有一批内置命令和 shell 行为会被显式禁止。简单的理解是:普通 Bash 里的"改环境、换目录、重定向、替换进程"等能力,在受限模式下被大面积收紧了。
有个特别容易忽略的点是:受限模式不仅影响交互命令,也会影响启动文件。如果启动文件里含有限制不允许的操作,Bash 执行到那一步时会直接报错,但不会因此拒绝启动——这和我们后面要讲的 bashrc permission denied 问题有很强的关联,后面展开说。
| 能力 | 普通 Bash | Restricted Shell |
|---|---|---|
cd 切换目录 |
允许 | 禁止 |
修改 PATH、SHELL、ENV 等变量 |
允许 | 禁止或忽略 |
执行带 / 的绝对/相对路径命令 |
允许 | 禁止 |
使用 > 等操作符重定向到文件 |
允许 | 禁止 |
用 exec 替换当前 shell 进程 |
允许 | 禁止 |
用 enable -f 加载外部内置命令 |
允许 | 禁止 |
| 关闭受限模式 | 视情况 | 禁止 |
这张表基本概括了受限模式的骨架。下面我们逐个拆开,看每一条限制背后到底在防什么。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 受限 Shell 到底把哪些门焊死了
2.1 被禁用的命令与行为清单
我们来完整过一遍 Bash 手册里列出的受限模式限制项,每一步我都补上"为什么要有这条"的解释。
第一,cd 被禁止。含义非常直接:你无法改变当前工作目录。这个限制防止用户在登录后逛到别的目录里,比如 /etc、/root 或者其他用户的 home。实际操作中,cd 一旦被禁,用户所有操作都只能围绕当前所在的登录目录展开,行为半径一下子收缩了很多。
第二,SHELL、PATH、ENV、BASH_ENV 这几个变量不能被设置或取消。这里的关键不是"这几个变量很重要",而是这几个变量几乎控制了 shell 的一切行为来源。PATH 决定了你能执行什么程序,ENV 和 BASH_ENV 决定了启动 shell 时会加载哪些额外配置。把它们锁死,等于把环境入口焊住了。受限模式下尝试给这些变量赋值会被静默忽略,尝试 unset 会被直接拒绝。
第三,不能执行任何包含 / 的命令名。这条非常反直觉,但很有效。普通模式下你可以敲 /bin/ls 或 ./script.sh,受限模式下这种写法直接报错。这意味着用户无法通过绝对路径去
