Linux只读用户创建与权限控制实战指南
2026/9/19 2:06:45 网站建设 项目流程

做运维这些年,我发现一个特别高频的需求:给某个同事、某个外包、或者某个临时来看日志的人开一个账号,让他能看系统里的东西,但又绝对不能改东西。大部分人第一反应是“给他一个账号不就行了”,结果没过两天,要么目录被误删,要么配置文件被改乱,要么人家顺手执行了一条高风险命令,运维只能连夜擦屁股。

这里就引出一个标准操作:在Linux上创建一个普通只读用户。这篇文章我不讲花哨的理论,就按实际运维场景来拆——先搞清楚Linux用户权限模型是怎么回事,然后一步步把账号建出来,再做细粒度权限控制,最后把常见问题和排查命令整理成一份能直接抄的清单。

1. 为什么需要只读用户:先搞懂权限模型

1.1 典型场景:谁需要只读账号

先说说我在实际工作中遇到过的三类需求。

第一类是临时协作者。比如公司请了外部顾问来排查性能问题,他需要看系统当前的进程、日志、配置参数,但你不想让他动任何东西。第二类是跨部门查看。比如质量部门要审计项目目录下的报表文件,他们只负责看,不负责改。第三类是运维自身的日常巡检账号。用root或高权限账号登录巡检,风险太高,一旦手滑执行了rm -rf这类命令,后果不用我多说。所以一个专门用于只读巡检的账号,在正规环境里几乎是标配。

这个需求背后的核心矛盾是:既要给用户足够的“看”的权限,又要彻底剥夺“写”和“执行”的能力。很多新手会以为“只读”就是把文件权限设成400,其实远没有这么简单——目录的权限、父目录的权限、sudo的配置、Shell环境的限制,每一样都可能导致要么看不了,要么能改东西,总有一个坑等着你。

1.2 先把Linux用户、用户组、权限这三件事理清楚

Linux的权限模型,我用一句话概括:你是谁(用户),你在哪个圈子(组),你对一个文件能做什么(读、写、执行)

每个文件或目录,都有一组“三元权限”,分别对应三种身份:属主(user)、属组(group)、其他人(other)。每个身份又对应三个权限位:

权限对文件对目录
r(读)能查看文件内容能列出目录下有哪些文件名
w(写)能修改文件内容能在目录中新增、删除、重命名文件
x(执行)能运行该文件能进入该目录,也能访问目录下子目录和文件

注意看上表最后一行的差异:对目录来说,光有r权限还不够,想真正进到目录里面去,需要x权限。很多只读用户“能ls但无法cd”的问题,根源就是父目录缺了x权限。

用户组的意义在于批量管理。给一组人统一授权,只需要把他们都放进同一个组,然后给这个组赋予权限就行,不用一个一个用户去设权限。创建只读用户时,单独建一个readonly组,再让这个组去对应目标目录,这是最清晰的做法。

1.3 只读用户的技术边界:要认清“能读就能复制”

在动手之前,必须先说清楚一个容易被忽略的事实:Linux层面你无法做到“禁止复制内容”。如果用户对某个文件有r权限,他就可以用cat file > /tmp/copy把内容复制走。所以“只读”的目标不是防泄露,而是防止误修改、误删除、误执行。如果核心诉求是防拷贝,那就不能靠Linux权限解决,得上加密、DLP一类的方案。

我在生产环境给客户设计只读账号时,经常先跟他们确认这一点:只读不等于沙箱,也不是安全隔离。它解决的是“别让我手滑改坏东西”的问题,防的是无心之失,不是蓄意攻击。把这个期望值拉对,后续的方案才不容易跑偏。

2. 创建只读用户的标准操作流程

2.1 第一步:创建用户并禁止登录Shell

创建只读用户的第一个动作,是用useradd建账号,并指定Shell为/sbin/nologin

useradd -m -d /home/reader -s /sbin/nologin reader

参数说明一下:-m表示创建用户家目录,-d指定家目录路径,-s指定登录Shell。这里最关键的选项是-s /sbin/nologin,意思是这个账号不能交互式登录系统。用户被禁止登录Shell后,就算拿到密码,也无法通过SSH或者终端进入系统执行命令。

有些教程推荐用/bin/false来禁止登录,区别在哪?/bin/false的特点是直接返回一个非零退出码,会话会立刻断开;/sbin/nologin则会输出一行提示信息再断开,对使用者更友好。我建议用/sbin/nologin

那有人会问:既然不能登录Shell,那这个账号怎么“看”文件?有两种方式:一种是通过SFTP或某些只支持文件传输的工具连接(这需要另行配置,SSH连接会被nologin挡住);另一种是配合sudoers白名单,允许它只执行少数几个只读命令。我一般更推荐第二种,后面第三部分细讲。

2.2 第二步:设置密码与账号过期策略

设置密码用passwd

passwd reader

如果你希望这个账号只能在某段时间内有效,可以在创建时就指定过期日期。比如让账号在2025年12月31日过期:

useradd -m -d /home/reader -s /sbin/nologin -e 2025-12-31 reader

-e参数的效果是,时间一到,账号自动锁定,用户无法再登录。这个参数对临时协作账号特别有用,省得你用完忘记清理。

比你预期多活了一阵子的临时账号,是运维安全事故的高发地带。我的习惯是:所有临时账号都设置过期时间,宁可后面发现还需要用再延,也不要让它无限期躺在那儿。

2.3 第三步:加入特定组并限制sudo

不建议直接在默认组里改来改去,单独建一个只读组更清爽:

groupadd readonly usermod -aG readonly reader

重点在于-aG中的a,也就是append,追加到组里,而不是把用户的主组改成readonly。这个细节很多新手会忽略,少了-a可能导致用户被移出原有组,引发其他权限异常。

限制sudo的方法很简单粗暴:默认就不给这个用户sudo权限。Linux下sudo权限由/etc/sudoers控制,绝大部分情况下,只读用户不应该出现在这个文件里。如果你检查后发现,安装某些软件时把用户默认加进了wheelsudo组,那就需要把这个组权限也拿掉,否则用户依然可以提权。

查看用户当前所属组:

groups reader

如果输出里有sudowheel,用以下命令移除:

gpasswd -d reader sudo gpasswd -d reader wheel

2.4 第四步:目录只读授权与测试

假设有一个目录/data/app/logs,我们希望reader用户能进去看日志,但不能改动任何文件。

先看当前目录权限:

ls -ld /data/app/logs

如果属主和属组都不是root,需要先调整,然后给readonly组赋权。只读含义是:目录本身要有r和x权限,目录里所有文件要有r权限

用一条命令给只读组授权目录:

setfacl -R -m g:readonly:rX /data/app/logs

这里用ACL(访问控制列表)而非传统chmod,是因为ACL能把授权对象精确到组或用户,不需要同时修改属组。-R表示递归处理所有子目录和文件,g:readonly:rX意思是:授予readonly组读权限,目录额外给予执行权限(X大写表示仅对目录添加执行位,不会给普通文件添加执行权限)。

然后验证:

sudo -u reader ls -l /data/app/logs

但这里有个问题:sudo -u需要你当前账号有sudo权限,而且reader被nologin限制后,正常情况下无法直接执行命令。所以我们还需要一种测试方式:临时给reader一个可以用的Shell,验证完之后再改回来,或者直接用root切换到reader身份查看。

从root切换并测试的完整命令:

su -s /bin/bash reader -c "ls -l /data/app/logs"

注意,su -s /bin/bash reader是临时指定用bash作为Shell来切换,这是为了在root环境下模拟用户身份执行命令,测试结束后reader的登录Shell仍然是nologin,不影响安全性。

2.5 一个可以直接抄的完整创建序列

把所有步骤串联起来,就是一段可直接执行的脚本:

# 1. 创建只读组 groupadd readonly # 2. 创建用户,指定nologin,设置1个月后过期 useradd -m -d /home/reader -s /sbin/nologin -e $(date -d "+30 days" +%Y-%m-%d) reader # 3. 设置密码(会提示交互输入两次) passwd reader # 4. 追加到只读组 usermod -aG readonly reader # 5. 从高权限组移除(如果存在) gpasswd -d reader sudo 2>/dev/null gpasswd -d reader wheel 2>/dev/null # 6. 授予目标目录只读访问权限 setfacl -R -m g:readonly:rX /data/app/logs # 7. 测试 su -s /bin/bash reader -c "ls -l /data/app/logs"

这套流程走完,reader用户的基本只读能力已经具备了。如果只是临时简单用用,到这一步就够。但如果这个账号需要被不止一个人使用,或者要严格控制它能接触的命令,那就继续往下看第三部分。

3. 细粒度权限控制:把“只读”做到极致

3.1 区分r--与r-x:隐藏的执行风险

很多运维在设置只读权限时,习惯性给目标目录chmod -R o+r,这是不够的。目录要想被访问和遍历,必须要有x权限,否则用户连目录都进不去。所以对目录至少需要r-x,即权限位为r-x。但对目录中的普通文件,应该只给r--,不给执行位。这就是我在上一节使用大写X的原因:批量设置时,自动跳过普通文件的执行位。

“隐藏的执行风险”指的是另一种情况:如果目录中存在脚本文件,比如backup.sh,而文件权限里带了x,那么只读用户虽然不能修改这个脚本,却可以触发它执行。很多日志目录里恰好存着一些带x位的辅助脚本,如果不加区分地授权,等于变相给了用户执行权。

检查目标目录下的可执行文件:

find /data/app/logs -type f -perm /111 -exec ls -l {} \;

如果确认这些脚本不需要被执行,可以用find配合chmod批量清理执行位。注意只处理普通文件,别动目录的执行位:

find /data/app/logs -type f -exec chmod a-x {} \;

3.2 用ACL实现多用户差异化授权

如果系统里有多个只读用户,但各自能看的目录不一样,传统权限分组会变得很笨重。ACL在这里就很有用了。

比如我们有三个用户:reader1可以看/data/app/logs,reader2可以看/data/backup,reader3两个目录都能看。不用创建一堆组,直接对用户设置ACL:

setfacl -R -m u:reader1:rX /data/app/logs setfacl -R -m u:reader2:rX /data/backup setfacl -R -m u:reader3:rX /data/app/logs /data/backup

查看现有ACL权限:

getfacl /data/app/logs

移除某个用户的ACL授权:

setfacl -R -x u:reader1 /data/app/logs

使用ACL时有个建议:给每个只读用户都单独建一个组,ACL只授权到组,不直接授权到用户。比如groupadd readonly-applogs,然后把reader1和reader3放进这个组,再setfacl -R -m g:readonly-applogs:rX /data/app/logs。这样管理粒度更清晰,以后新增用户只需要加组,不用改目录的ACL。

3.3 防止sudo和su越权

之前提到默认不给sudo,但某些场景下,业务方确实需要只读用户去运行几个指定的查询命令,比如查看磁盘空间、查看内存状态:

df -h free -m ps aux

这时候可以针对特定命令做sudo白名单。编辑sudoers配置:

visudo

增加一行白名单,注意%readonly表示readonly组,ALL=(ALL)表示可以在所有主机上执行,后半部分是允许的命令完整路径列表,多个命令用逗号分隔:

%readonly ALL=(ALL) /usr/bin/df, /usr/bin/free, /usr/bin/ps

配置完成后,reader用户可以通过sudo /usr/bin/df -h查看磁盘,但无法执行sudo vim或者sudo bash这一类高危命令。

这里必须提醒一个易被忽视的命令:less 这类分页工具不能轻易放进白名单。因为less内部支持执行Shell命令,比如在less界面中输入!bash就能起一个Shell。vi/vim同理。我之前见过一个团队把catless都加了白名单,结果等于把整台机器交了出去。如果确实需要查看日志文件,宁可单独写一个受控脚本,封装好查看逻辑,再把脚本放到一个不可写的目录里,然后给用户执行该脚本的sudo权限。

验证sudo配置是否正确:

sudo -l -U reader

这条命令会列出reader这个用户可以执行的sudo命令清单,是排查越权问题的常用利器。

3.4 隐藏文件、符号链接与软硬链接的安全陷阱

授权目录时,-R参数会递归处理子目录,但默认不会复制隐藏文件。很多人配置完ACL后,只读用户列目录能看普通文件,却看不到.env.git这类隐藏文件。解决办法是手动确认隐藏文件也有对应权限,或者在使用setfacl之前先用find把隐藏文件也一起授权:

find /data/app/logs -name ".*" -exec setfacl -m g:readonly:r {} \;

另一个坑是符号链接(symlink)。Linux下,对符号链接本身设置的权限是无效的,实际生效的是它指向的目标文件的权限。举个例子:/data/app/logs/current.log是指向/data/app/logs/2025-06-01.log的软链,那么reader用户能否读取current.log,取决于2025-06-01.log的权限,而不是软链自身的权限。排查问题时,要使用readlink -f解析真实路径。

硬链接更隐蔽。硬链接文件与原始文件共享同一个inode,修改任何一个都会影响另一个。如果只读用户对某个文件只有r权限,但系统里有另一个属于该用户的同名硬链接,情况不会变复杂,但至少要知道这一点,避免在审计时被表面路径绕晕。

3.5 审计与下线:让权限收得回

创建只读用户只是开始,账号的生命周期管理更重要。在/etc/sudoers中配置了命令白名单的用户,所有sudo调用都会记录在/var/log/auth.log(Debian/Ubuntu)或/var/log/secure(RHEL/CentOS)里。查看某个只读用户最近执行过哪些sudo命令:

grep "reader" /var/log/auth.log | grep "COMMAND"

普通登录记录用lastloglast查看:

lastlog -u reader last -u reader

账号到期自动锁定的策略,前面已经通过-e参数实现了。如果需要手动下线账号,用下面两条命令之一即可:

# 立即锁定,但账号信息保留 usermod -L reader # 彻底删除用户(谨慎操作,会连用户主目录一起删) userdel -r reader

我的建议是,临时协作结束后先用usermod -L锁定账号,观察一段时间确认无影响后再删除。

4. 常见问题与排查技巧实录

4.1 用户登录后提示Permission denied

这是最常见的报错。现象是只读用户通过su或者SFTP连接后,执行cd /data时提示 permission denied。

排查思路:检查目录链路上每一级的权限,不只是目标目录。比如目标路径是/data/app/logs,那//data/data/app/data/app/logs每一级目录都需要为只读用户开放x权限。用一条命令就能看全链路的权限:

namei -l /data/app/logs

namei 会输出从根路径到目标目录每一级目录的权限,哪一级缺了什么一目了然。修复方式:为每一级父目录追加对应权限:

setfacl -m g:readonly:x /data setfacl -m g:readonly:rX /data/app

父目录只给x不给r,这样用户能“经过”该目录,但看不到目录下有哪些文件,对系统其他路径不产生多余的可见性。

4.2 能进目录却看不到文件

这种问题比上一种更隐蔽。用户能cd进目录,但执行ls后一片空白。

原因一般是目录有x权限但缺r权限。x权限保证能进入,r权限才用来列出文件名。修复:

setfacl -m g:readonly:rX /data/app/logs

这里再次体现大写X的价值——给目录补r和x,给普通文件只补r,安全又省事。

4.3 sudoers写错了导致所有用户无法sudo

修改/etc/sudoers时,语法错误会导致所有sudo操作直接失效,包括root。虽然这是所有Linux运维都该知道的问题,但我在实际中见过太多人在这里翻车。

正确姿势是:修改sudoers只用visudo。visudo在保存时会做语法检查,有错误会拒绝保存,这是最后一道防线。如果已经发生了修改错误导致sudo不可用,而你还有root登录窗口,那就用root直接修正文件;如果root也被锁在sudo之外,可以在单用户模式下修复文件。这里的关键教训是:改sudoers之前备份一份cp /etc/sudoers /etc/sudoers.bak,能让你在出问题时快速恢复。

4.4 只读用户却能写文件?检查umask和ACL

有时候授权配好了,用户发现自己居然能在某些目录创建文件,这是怎么回事?

第一个要查的是ACL默认权限。setfacl -R -m只改现有文件,但目录如果设置了默认ACL(default ACL),新创建的文件会自动继承权限。检查目录的默认ACL:

getfacl /data/app/logs

输出里如果有default:group:readonly:rwx之类的行,说明新文件会继承写权限。清理默认ACL:

setfacl -k /data/app/logs

第二个要查的是补集权限。如果你对某个目录同时设置过chmod o+w或者之前的ACL没有清理干净,要在授权前先清除其他无关权限位。稳妥做法是:先移除该目录上可能的“其他人”写权限,再单独设置ACL:

chmod o-w /data/app/logs setfacl -R -m g:readonly:rX /data/app/logs

第三个隐藏因素:sudo白名单里有没有写命令。如果sudoers里被允许执行的命令里包含了installteedd这类能写文件的命令,只读用户就能通过sudo绕过目录权限。检查一下:

sudo -l -U reader

看到输出里有能落盘的工具,就要重新审视白名单了。

4.5 中文文件名显示乱码的排查

很多只读用户看日志时,遇到中文文件名输出乱码,这其实是终端字符集的问题,不是权限问题。系统locale默认可能是POSIX或C,无法正确解读UTF-8编码的文件名。

临时解决:

export LC_ALL=C.UTF-8

或者使用支持控制字符显示的ls参数:

ls --show-control-chars

更建议在用户家目录的.bashrc中固定写入:

export LC_ALL=C.UTF-8 export LANG=C.UTF-8

虽然只读用户通常不能交互式登录Shell,但在sudo白名单命令中如果包含需要输出中文内容的工具,这个配置可以有效避免乱码干扰。

4.6 快速定位权限问题的一组命令

把我在排查权限问题时最常用的命令整理成一张速查表:

排查目标命令说明
用户身份与组id reader查看UID、GID、所属组
完整路径权限链路namei -l /data/app/logs逐级显示目录权限
目标文件ACLgetfacl /data/app/logs查看ACL设置
sudo命令白名单sudo -l -U reader查看该用户可执行的sudo命令
所有含可执行位的文件find /data/app/logs -type f -perm /111找出危险性文件
登录历史lastlog -u reader查看最近登录时间
sudo调用记录grep "reader" /var/log/auth.log审计用户sudo命令

这套命令配合使用,90%的权限问题都能在五分钟内定位到根因。

5. 从单机到集群:批量创建只读用户的思路

5.1 用脚本实现一键创建

单台机器手动操作够用,但如果是生产环境十几台机器都要开只读账号,手敲命令效率太低,而且容易漏步骤。可以写一个简单的脚本,一次创建多个用户。

/root/create_readonly_users.sh中写入:

#!/bin/bash # 批量创建只读用户 # 用法: ./create_readonly_users.sh user1 user2 user3 READONLY_GROUP="readonly" TARGET_DIR="/data/app/logs" if [ "$(id -u)" -ne 0 ]; then echo "请使用root运行此脚本" exit 1 fi # 确保只读组存在 getent group "$READONLY_GROUP" >/dev/null || groupadd "$READONLY_GROUP" for user in "$@"; do if id "$user" >/dev/null 2>&1; then echo "[跳过] 用户 $user 已存在" continue fi useradd -m -d "/home/$user" -s /sbin/nologin -e "$(date -d '+30 days' +%Y-%m-%d)" "$user" usermod -aG "$READONLY_GROUP" "$user" passwd -l "$user" >/dev/null 2>&1 echo "[创建] $user 已创建并锁定密码(请用 chpasswd 设置初始密码)" done # 批量授权目标目录 setfacl -R -m "g:$READONLY_GROUP:rX" "$TARGET_DIR" echo "目录授权完成:$TARGET_DIR"

脚本里我特意用了passwd -l先把密码锁定,防止脚本执行期间账号处于无密码状态被滥用。之后再单独设置初始密码并告知对应使用者。

5.2 多机器同步用户与sudo配置

单机脚本解决单机问题,跨机器同步就得上配置管理工具。Ansible在这个场景下是最顺手的,它对运维人员友好,没有agent依赖,只需要控制机能ssh到目标机器。

核心思路是:把“创建只读用户”做成一个Ansible playbook,目标机器清单放在hosts文件里,用户列表放在变量文件里。执行一次playbook,所有机器都会同步创建账号并配置好权限。

--- - hosts: all become: yes vars: readonly_group: readonly target_dirs: - /data/app/logs readonly_users: - reader tasks: - name: 创建只读组 group: name: "{{ readonly_group }}" state: present - name: 创建只读用户 user: name: "{{ item }}" shell: /sbin/nologin create_home: yes groups: "{{ readonly_group }}" append: yes expires: "{{ lookup('pipe', 'date -d \"+30 days\" +%Y-%m-%d') }}" loop: "{{ readonly_users }}" - name: 授权目标目录只读权限 acl: path: "{{ item }}" entry: "group:{{ readonly_group }}:rX" recursive: yes state: present loop: "{{ target_dirs }}"

playbook的好处是幂等——重复执行不会重复创建账号或者反复叠加权限,这在批量管理场景里非常实用。

引入配置管理工具虽然有学习成本,但长远看,它的价值不只是省时间,更重要的是把权限配置变成了代码,任何人通过修改配置仓库都能做同样的事情,不再依赖某一个人的手工操作记忆。

5.3 让只读用户与监控审计打通

账号建好了,如果不监控它的使用情况,等于白建。生产环境建议把这几个监控点拉起来:

第一,登录与sudo日志采集。用现有日志系统收集/var/log/auth.log/var/log/secure,针对只读用户单独设置告警规则,一旦出现非工作时间的大量登录,或者执行敏感命令,立刻触发告警。

第二,定期复核账号有效性。写一个定时任务,每周检查一次即将过期的只读账号,输出通知:

0 9 * * 1 awk -F: '($2!="!" && $2!="*") {print $1}' /etc/shadow

这条命令列出所有有密码的非锁定账号,但更直接的做法是用chage -l reader查看单个账号的过期信息。

第三,启用命令审计。如果企业内部有安全要求,可以给只读用户配置bash的history记录,把每个账号执行过的命令统一记录到独立文件。虽然只读用户被nologin限制不能交互登录,但一旦有sudo白名单命令,这些调用都会被系统日志记录下来,配合第一步就够了。

5.4 经验建议:先列清单再动手

我踩过很多次坑之后总结出一个习惯:在处理权限需求时,不要直接敲命令,先把下面五个问题列出来:

  • 这个账号给谁用?是个人还是多人共用?
  • 需要看哪几个目录?需要看哪些具体文件?
  • 是否需要执行某些命令?必须是白名单,还是完全不需要?
  • 有效期多久?到期自动锁定还是手动清理?
  • 是否考虑审计?需要保留哪些操作日志?

把这些问题回答完,方案基本已经成型了。然后再去落地第二步、第三步的命令,你会发现思路特别清晰,基本不会出现“建完账号发现用户没权限看目录”这种返工情况。

我在给客户做这套方案时,最喜欢说的一句话是:权限管理不怕慢,就怕反反复复改。把目标目录的清单和用户清单一次性列准,后续所有授权都是一通操作的事情。

回到只读用户本身,这里再分享一个小技巧。创建完成后,可以在用户家目录放一个提示文件,比如/home/reader/README.txt,内容写明“该账号为只读权限,仅限查看,禁止修改”。当用户登录看到提示后,误操作的概率会下降不少。网络上有大量关于Linux用户和用户组权限的讨论,但真正靠谱的做法往往是那些最基础、最朴素的规范操作,而不是到处找“捷径”。先把标准动作做对,再考虑怎么优化,这就是我处理Linux只读用户问题的核心经验。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询