开头直接用从业者口吻结合自身经历引入主题点出核心场景运维巡检、日志查看、给外部同事开一个“只读账号”同时点名关键技术useradd、sudo、mount只读挂载。然后自然过渡到正文。只读用户不只是chmod 444那么简单做运维这些年开过最多的账号大概就是“只读用户”。需求基本都长一个样某人需要上服务器看看日志、查查进程、确认一下配置但我们不想让他动任何东西最好连删个临时文件的能力都没有。这类账号最典型的使用场景是给审计同事一个只读账号去机房巡检、给研发同事开个账号只查日志不碰服务、给外部合作伙伴临时开个只读账号让他们自己排查网络问题。听起来很简单对不对不就是创建一个普通用户、不给他sudo权限嘛。真做过一遍你就会发现Linux里的“只读”是个很暧昧的词系统层面根本没有“全局只读”这个开关你得从账号权限、文件系统挂载、命令执行策略三个层面一起动手才能真正让用户“看得见但摸不着”。这篇文章就围绕“linux创建普通只读用户”这件事把我平时常用的组合方案一步步拆开讲透。包含useradd参数选型、密码锁定策略、sudo白名单怎么写才不踩坑、目录只读挂载的持久化配置以及最后那些“看起来没问题但一登录就暴露”的坑。内容不算很深但都是我实际敲过的命令适合刚接触Linux账号管理的同学也适合想规范服务器账号体系的人翻出来参考。1. 需求拆解什么场景下才需要“只读用户”创建只读用户前先搞清楚一件事你到底是防“手滑”还是防“恶意”这是两条完全不同的技术路线。如果是防手滑——比如自己或同事在正式环境里误删了配置文件、误改了服务参数那我们要做的就是一个“尽量少权限”的账号。它本质上是靠权限管理降低人为失误的概率属于运维规范层面的东西。这种需求不用做得太绝只要把写权限、执行权限收干净配合sudo白名单就能覆盖大部分场景。如果是防恶意——比如怀疑有人拿到账号后会故意搞破坏、传文件、扫内网那情况就复杂得多。普通用户权限下的“只读”根本不是真正的安全边界。你可以用chmod去掉写权限但对方完全可以下载一个不依赖文件写权限的工具来反弹shell你可以限制sudo但对方如果利用系统漏洞获取root那你所有只读限制都是形同虚设。所以做安全加固时只读账号只是基础还要配合网络隔离、审计日志、命令堡垒机等一整套机制。我自己的经验是绝大多数业务方要的其实都是第一种“防手滑”。所以下文会先把基础方案讲透再给出一个实践中最稳妥的组合推荐最后单独强调哪些场景下“只读用户”这个方案压根不够用别指望它当安全防御用。我们先看第一种需求下最简单的实现层次。所谓“只读”落到具体动作上无非是三类不能新增、删除、重命名文件不能修改已存在文件的内容不能启动会改变系统状态的命令。对应到Linux技术栈就是账号权限、文件系统挂载策略、sudo命令白名单三个方面的事。熟练之后三件事分开来做条理非常清晰。2. 基础账号创建useradd参数选型与密码锁定策略创建用户本身很简单一句话useradd -M -s /usr/sbin/nologin readonly_user。但真正生产环境里我几乎不会用默认useradd参数直接建号因为默认行为里藏着好几个隐患。默认的useradd会同时创建用户家目录、会使用系统默认shell通常是bash还会按/etc/login.defs里的配置给用户设置密码时效。这些默认值对“只读用户”来说都不够理想。我给这类账号用的命令是这样的useradd -M -s /bin/bash -U readonly_user先解释几个参数-M不自动创建家目录。为什么不建家目录因为家目录默认是普通用户自己的读写区域如果创建了用户登录进去就能在自己的home里随便建文件。虽说不影响系统全局但很多“只读”需求连这个也不想留。而且很多程序启动时会往HOME目录写缓存不设家目录反而能避免一部分“运行时偷偷改东西”的场景。-s /bin/bash给一个可用的shell。很多人会想既然是只读用户直接给它/usr/sbin/nologin让他登录不了不就完了这个想法在“纯只看文件”的场景是对的但现实需求往往是“我需要他能登录上来敲几条查看命令”。nologin意味着SSH登录直接被拒不符合需求。所以这里先用bash后面通过sudo规则和rbash来限制这个取舍在第三节细说。-U同时创建同名用户组。后续给权限时可以用组为单位管理比单个用户操作更灵活。建完账号后密码策略根据使用方式有两种选择第一种如果这个账号只给一个人用且对方通过SSH密钥登录那我倾向于直接锁定密码passwd -l readonly_user锁定密码后该账号无法通过密码登录只能靠预置的SSH公钥认证。好处很明显不存在密码泄露的问题也不存在弱口令爆破的风险。第二种如果对方确实需要密码登录比如对方在自己电脑上、我们没法控制他的客户端配置那就用强密码同时设置短时效passwd readonly_user chage -M 30 readonly_userchage -M 30表示30天后必须改密码。对这种低频使用的只读账号密码长期不换就是个定时炸弹建议至少给个有效期。另外把空密码策略关掉passwd -l root 的相反方向需要注意这里提醒一个坑passwd -l在CentOS 6和早期系统上是向/etc/shadow密码字段前面加!!效果可靠。但在某些较新的系统上passwd -l其实只锁定了密码登录不影响SSH密钥登录和其他PAM认证方式。这个不一定是坏事只是要心里有数——锁定密码不等同于“锁死账号”只是禁用了密码认证这一种方式。账号建好之后检查一下端口确认真实shell是bash而不是被nologin挡掉、确认家目录没有自动生成。如果useradd版本默认配置与预期不一致可以用grep -E CREATE_HOME|SHELL /etc/login.defs查看系统默认值明白默认行为再决定加不加参数。3. 权限收敛文件系统层面的“只读”账号建好后最核心的问题是这个用户登录进去到底能碰哪些目录。默认情况下普通用户对/etc、/usr、/opt这些系统目录只有读权限写不了什么。真正需要处理的是三类地方用户自己的家目录、/tmp这类全局可写目录、以及你要开放给他查看但又不希望他改的业务数据目录。3.1 家目录与全局可写目录的处理如果按上一节操作加了-M用户没有家目录登录时shell会报“Could not chdir to home directory”然后以/作为当前目录。这个报错不影响命令执行但每次登录都提示体验不好而且某些程序依赖HOME环境变量设置为不存在的路径可能引发额外问题。我一般建议给只读用户建一个家目录但把目录权限做成只读mkdir -p /home/readonly_user chown readonly_user:readonly_user /home/readonly_user chmod 555 /home/readonly_userchmod 555就是r-xr-xr-x用户自己也没写权限。但你可能会问“他自己的家目录都写不了那shell启动时要写.bash_history怎么办”答案很简单——它写不了就写不了顶多不记录历史命令。如果非要历史记录就单独指定一个可写的小目录但我会明确建议只读用户不需要历史记录抹掉反而更干净。相比之下/tmp是个真正需要注意的洞。/tmp对所有人开放写权限任何用户都能在里面创建文件。如果你严格追求“不能被写入”那就得用只读挂载把/tmp变成只读但这个动作很可能会影响系统正常运行——很多服务、临时文件、socket都在/tmp下。这就回到了“防手滑”和“防恶意”的取舍。对防手滑场景我不建议动/tmp因为改动成本太高对防恶意场景只读挂载/tmp也没多大用攻击者可以换一个可写目录。后面第6节还会专门讲这个矛盾。3.2 关键业务目录的只读挂载如果要开放的目录本身需要被用户写比如业务程序要写日志目录、要写临时文件但又不想让只读用户去改历史数据那就要用到Linux的bind mount配合remount只读。它的核心思路是把系统里的某个目录重新挂载一份到另一个挂载点并附加ro属性。这条命令是mount --bind /data/logs /data/logs_ro mount -o remount,ro,bind /data/logs_ro第一行把/data/logs绑定到/data/logs_ro两个路径指向同一份数据第二行把绑定后的挂载点重新挂载成只读。之后/data/logs_ro看到的内容和/data/logs完全一致但任何写入尝试都会被拒绝。为什么需要bind mount而不是直接mount -o remount,ro /data/logs因为后者会直接把原始目录变成只读影响业务写入。bind mount把同一个文件系统“映射”出一个只读视角原始目录的访问方式完全不受影响。这个技巧在给只读用户开日志目录时非常实用。生产环境里这种绑定挂载一般是写在/etc/fstab里的否则重启即失效/data/logs /data/logs_ro none bind 0 0 /data/logs_ro /data/logs_ro none bind,ro 0 0fstab里写两行的原因在于单独一行bind不会带上ro属性需要把绑定后的挂载点再用带ro的bind规则覆盖一次。如果你用systemd比较熟也可以写成mount unit效果更可控但fstab两行式最通用不依赖systemd版本。需要注意只读挂载挡得住普通用户挡不住root。root对任何只读挂载点都有强制写的能力可以remount成rw所以这个机制本质上是约束普通操作不是安全边界。另外业务程序本身如果还在写/data/logs那你通过/data/logs_ro看到的最新日志会有几秒到几分钟的延迟这取决于写缓存和挂载传播属于正常现象不用奇怪。3.3 文件权限和不可变位更细粒度的写保护除了挂载只读更细的权限控制也有必要。比如某个配置文件你想让只读用户能看、能执行某些脚本但绝对不能改可以用chmod把写权限收掉chmod 444 /etc/nginx/nginx.conf chattr i /etc/nginx/nginx.confchattr i给文件加了immutable位。加了这层保护后即使root用户也无法直接修改该文件得先chattr -i解除。这比普通chmod更强适合锁定核心配置文件。但有几点要注意chattr i只对ext4、xfs等部分文件系统有效对tmpfs、网络文件系统可能不生效另外有些程序重装或升级时会因为无法改写配置而报错所以这个操作只建议在“确定不需要改动”的文件上用。我遇到过一个案例某同事给整个/etc目录递归加了chattr i结果第二天系统更新时一堆包管理器因为无法覆盖配置文件而失败。所以不可变位这个手段要用在点子上千万别冲动到对目录整体操作。给单个配置文件加没问题批量加之前先想想系统更新怎么办。4. sudo白名单让用户只能执行“查看类”命令文件层面的“只读”解决的是“能不能修改文件”的问题但用户登录后能执行什么命令是另一套独立体系。默认情况下普通用户能执行大部分“非特权”命令包括passwd改自己密码、mount查挂载信息、su尝试切用户等。这里面有些命令是必须禁掉的有些则要单独放行。控制这个的入口就是sudo配置。我强烈建议不要直接编辑/etc/sudoers主文件而是到/etc/sudoers.d/下新建一个独立文件这样既方便管理也不容易改坏主配置。推荐做法visudo -f /etc/sudoers.d/readonly_user为什么用visudo因为它是官方推荐的编辑器保存时会做语法校验能避免因为sudoers语法错误导致所有sudo操作不可用的极端情况。我见过有人直接vim编辑sudoers然后写错语法结果整台服务器的sudo都失效还好他开着另一个root会话才救回来。用visudo等于买了个“写错不生效”的保险。下面是一套我自己常用的只读用户sudo规则readonly_user ALL(root) NOPASSWD: /usr/bin/cat, /usr/bin/less, /usr/bin/tail, /usr/bin/head, /usr/bin/grep, /usr/bin/ps, /usr/bin/top -b -n 1, /usr/bin/df, /usr/bin/free, /usr/bin/du, /usr/bin/ls, /usr/bin/stat, /usr/bin/mount, /usr/bin/nice, /usr/bin/iostat, /usr/bin/vmstat, /usr/bin/ss, /usr/bin/ip addr show, /usr/bin/curl -s, /usr/bin/wget -O -详细解释几个关键点第一所有允许执行的命令必须写绝对路径。sudo在匹配命令时是精确到路径的只写cat会被判定为无权限。这是初学者最容易踩的坑。怎么查一个命令的绝对路径用which cat、type -a top先查出来。第二top这类交互命令要特别注意。top本身是交互式的直接放行等于给用户一个交互shell的跳板虽然跳板功能有限但终归不干净。所以我把top限制成top -b -n 1这种批处理单次执行模式即“一次性输出后退出”。这样用户可以用它看实时信息但不会进入交互界面。第三su命令是绝对要禁掉的。默认情况下如果只读用户知道root密码他完全可以用su -切到root绕过所有限制。同样要禁的还有sudo su、sudo bash、sudo vi这类命令。vi尤其危险因为vim里可以执行:!sh直接起shell到那时你所有权限方案都白搭了。禁命令的写法readonly_user ALL(root) !/usr/bin/su, !/usr/bin/sudo, !/usr/bin/bash, !/usr/bin/sh, !/usr/bin/vi, !/usr/bin/vim, !/usr/bin/nano有人可能问“前面允许了cat后面又禁bash优先级怎么算”sudo的规则是后者覆盖前者而且同一行内先匹配到哪条生效哪条。为了清晰我会把禁命令放在允许命令前面或单独一行。实际测试中我建议把禁用的交互shell命令放到规则最前面避免因为规则顺序导致意外的放行。不过这里有一个绕不过去的现实一旦允许了cat、less这种查看命令用户理论上可以通过这些工具读取系统里大量的敏感文件包括/etc/shadow如果root权限运行cat的话、其他用户的私钥、数据库配置里的密码等。所以只读用户这个方案天生就是基于信任的——信任对方不会做越界的事。如果你连这个信任都没有那需要的根本不是“只读用户”而是“跳板机审批流”。再补一个重要细节NOPASSWD:表示执行这些命令不需要再次输入密码。对只读用户我一般会加因为这类账号通常是半自动巡检或者快速查问题用的每次都要输密码很麻烦。但如果你追求更高的审计完整性可以去掉NOPASSWD:让sudo记录里多一次密码验证事件审计日志更完整。5. shell层面的限制与加固账号建了、sudo白名单配了、目录挂载也做了只读但别急着收工。普通用户登录后拿到的是一个完整bash shell这个shell本身有能力做不少事可以cd到任何目录、可以设置环境变量、可以执行PATH下的所有未受sudo限制的命令。这些能力里藏着几个容易被忽略的绕过路径。5.1 rbash比bash更“自闭”的受限shellLinux自带的rbashrestricted bash是bash的一种受限模式它限制用户在登录后不能cd切换目录只能待在当前目录及其子目录不能把PATH环境变量改成自定义值不能重定向输出到任意文件不能执行包含/的绝对路径命令只能执行PATH目录下的命令不能unset内置变量。创建时这样改用户的shellusermod -s /bin/rbash readonly_userrbash的思路不错但它并不能完全锁住用户绕过的案例一大堆。比如通过su readonly_user切另一个shell、或者利用ssh命令执行远端命令、或者在允许的命令里加上空格参数来隐式调用别的程序。所以rbash适合条件有限、权限要求不太严格的场合但对掌握Linux基本攻击手法的人来说形同虚设。严格防护的做法是用命令白名单配合特定入口比如系统本来就提供的rssh或者加固后的scp、sftp限制。不过我个人在实际项目中很少给只读用户上rbash因为它在降低风险的同时也严重影响了正常使用体验。5.2 环境变量与真实路径防护环境变量才是最容易被忽视的“旁路”。比如用户在自己的~/.bashrc里写export PATH/tmp:$PATH然后放一个假的ls脚本到/tmp之后他执行ls时就会跑到/tmp下的恶意脚本。这个脚本虽然以他自己的权限运行但在某些提权漏洞利用中这种“可控代码执行”是起点。怎么防第一步是给账号设置一个只读的、受控的PATH。在用户自己的~/.bash_profile里加export PATH/usr/local/bin:/usr/bin:/bin umask 027这里umask 027确保新建文件默认不给其他用户写权限降低“写文件后影响其他用户”的概率。第二步是把.bashrc的属主改成root并且设置为只读chown root:root /home/readonly_user/.bashrc chmod 644 /home/readonly_user/.bashrc这样用户无法修改自己的shell配置文件也就无法往里面植入恶意环境变量。但要注意.bashrc的属主如果改成root用户登录时bash读取该文件会把它当作“不可写”有些bash版本会忽略不可写的启动文件导致配置不加载。解决方法是仍然保留用户可读但使用readonly标记变量或者干脆把要保护的变量写死在/etc/profile.d/下的全局配置里并设置只读属性。这招更稳cat /etc/profile.d/readonly_env.sh EOF export PATH/usr/local/bin:/usr/bin:/bin export HISTFILE/dev/null export HISTSIZE0 EOF chmod 755 /etc/profile.d/readonly_env.sh设置HISTFILE/dev/null的意思是让所有历史记录写入空设备也就是不落地。对只读用户我很推荐这个配置——既保证不留痕迹也规避了~/.bash_history写失败的一堆报错。5.3 需要清醒认知shell限制的边界在哪里讲到这一步必须泼盆冷水。rbash也好、环境变量收口也好、sudo白名单也好都属于“用户态限制”不是“内核级隔离”。一个真正的攻击者只要能执行任意代码有无数种方法绕过这些限制。最常见的路径就是利用系统服务漏洞提权到root一旦拿到root你再多的只读取设置都可以被绕过。换句话说这套方案对“防手滑”是优秀的对“防攻击”是不够格的。安全界有个说法叫“least privilege”最小权限原则只读用户本质上就是最小权限原则在账号管理上的落地。它降低的是“误操作”和“无意识越权”的概率而不是“有意识攻击”的风险。6. 常见问题与排查实录讲了这么多理论下面把实际运维中我反复踩过的坑和排查过程整理成一个速查表。说实话这些坑每个都是拿真实线上环境换来的经验值得收藏。6.1 用户登录后显示“Could not chdir to home directory”这个最常见。用户认证成功但shell无法进入家目录提示Could not chdir to home directory: No such file or directory。原因通常是创建用户时用了-M而用户登进系统后shell尝试切到/home/readonly_user失败。解决方案在上文提过手动创建家目录并设置只读权限或者修改/etc/passwd里该用户的家目录指向一个已存在的目录如/。我个人建议前者因为家目录还是得有否则一些工具如mysql客户端的默认行为会异常。6.2 只读挂载不生效目录依然可写有次配置了fstab里bindro重启后居然还是可写的。排查过程是这样的先执行mount | grep readonly_ro查看挂载状态发现确实显示ro但试写入还是成功。后来查日志发现是overlayfs和内核版本兼容问题——某些旧内核在处理bind remount后文件系统表面显示ro但实际写入路径穿透到底层可写的overlay。解决办法是换用mount -o remount,bind,ro /data/logs_ro之后再用touch /data/logs_ro/test验证一次。这个“验证”步骤不能省别只看/proc/mounts的输出就放心了。6.3 sudo配置无效明明放行了cat却提示没有权限这个问题十有八九是路径没写对。用户用sudo cat /var/log/nginx/access.log被拒绝第一反应是查是否在sudoers里写了cat。用which cat查看cat的真实路径发现是/usr/bin/cat还是/bin/cat这在不同发行版上不一样。Debian系和CentOS 6的cat路径可能不同旧系统往往是/bin/cat。sudo规则必须和实际路径一致权限判断是字符串精确匹配不是靠PATH解析。另一个原因是规则的顺序问题。如果规则里同时写了/usr/bin/cat和!/bin/cat而该命令的路径在系统里被链接到/bin/cat那条禁命令规则反而会生效。所以检查时把sudo -l -U readonly_user的输出和which cat的结果对照一遍很快就能定位。6.4 只读用户能改自己的密码要禁吗默认情况下普通用户可以执行passwd修改自己密码。对只读用户来说这个能力的风险在于如果这个账号是给特定人用的修改密码意味着别人可能被挤掉或进不来。但反过来如果禁止改密码一旦密码过期又没法自助找回。我的建议是在账号创建时就决定好如果是为了配合密钥登录用passwd -l锁掉密码那么改密码的问题自然不存在如果必须保留密码登录那passwd还是得留因为允许改密码的运维成本远低于“密码过期后重置”的成本。6.5 chattr i带来的坑删不掉也改不了某个文件设了chattr i后来发现程序需要更新它结果root也写不了。解除方法很简单chattr -i /path/to/file但这个坑的本质是你给文件加了immutable位后之后任何自动化任务、备份工具、软件升级都可能会因无法写入而报错。如果文件很重要建议设完之后建立一套“变更记录”什么时间、为什么加只读位、由谁负责解除。排查问题时能省很多时间。6.6 “只读用户”其实还有局限root权限的程序不受控最后一个要牢记的认知是只读用户的所有限制都只作用于这个普通用户身份本身。如果系统上有其他root权限的进程比如Nginx worker、数据库服务、监控Agent这些进程对文件系统拥有完全的控制权。只读用户虽然权限很低但可以通过某些系统调用间接影响这些进程的运行比如短暂占满CPU、发送信号等。从严格安全角度看这不算一个真正的隔离环境。如果业务确实需要一个能被外部使用的“几乎完全隔离只读环境”我自己会考虑用systemd-nspawn或轻量级容器方案把用户限制在一个临时或只读的rootfs里。那是一条比普通用户只读挂载更强的路但它涉及镜像管理、网络配置和更复杂的权限映射一般仅在面向不可信对象的场景下才值得做适合以后单独开一篇来写。7. 写到最后只读账号的运维实战建议回到开头那个问题——“linux创建普通只读用户”到底怎么做才叫合格我个人最常用的一套组合如下用useradd -M -s /bin/bash创建无家目录用户密码锁定并用SSH密钥登录sudo白名单只放查看类命令并禁掉交互shell工具对要开放的业务目录做bindro挂载再加一层/etc/profile.d/全局环境变量收口。整套下来配置时间在十分钟以内效果却相当直观用户能看日志、查进程、确认端口但任何rm、vi、su、reboot都会被系统无情拒绝。这套方案的典型画像就是“给一个可信但容易手滑的人一份最小权限”。它能把运维操作面收得很窄也能在出事时让审计日志清楚地告诉你“这个用户做了什么”。但请记住它不是安全边界。如果对方恶意光靠这套是拦不住的。真要说我踩过最深的坑就是曾经把“只读用户”当作“安全隔离”来用结果对方利用一个业务服务的历史漏洞拿到了root所有只读机制瞬间化为乌有。从那以后我给自己定了一条规矩只读账号解决误操作安全隔离必须靠真正的隔离手段虚拟化、容器、网络策略两者永远不要混为一谈。如果你的需求就是给一个同事或审计人员开一个“看不见摸得着改不了”的账号照着本文这套思路操作应该足够应对绝大多数场景。最后再分享一个小习惯每次配置完只读用户我都会用这个账号重新登录一次跑一遍touch /tmp/test、rm -rf /home/readonly_user、sudo bash之类的动作确认全部被拒之后才算验收通过。这个测试过程只需要几分钟但能帮你提前发现那些配置时没注意的漏洞。别嫌麻烦线上出问题时再补成本就完全不一样了。