Ubuntu服务器默认的umask值是022,这意味着新建文件权限是644,新建目录是755。这个设置在多数场景下看似合理,但一旦服务器上有多个用户、Web应用、定时任务或自动化部署流程,权限问题就会暴露出来。644意味着同组用户和其他用户都能读取文件,而很多配置文件、数据库备份、包含敏感信息的日志往往不应该被所有系统账户看到。真正的问题不是权限不够用,而是默认权限过于宽松,且很多管理员在部署应用后从不审计目录归属和权限位。
精细化配置的第一步是明确服务器上的角色和目录归属。不要把所有文件都放在root或者某一个个人账户下,更不要让Web应用目录属于root而由www-data通过sudo写文件。正确做法是先规划目录:系统配置文件归root,Web根目录归部署用户,运行时可变数据归应用运行账户。例如/var/www/site目录可以设置为部署用户deploy所有,组为www-data,权限750;而/var/www/site/uploads这类需要Web进程写入的目录,归属设为www-data:www-data,权限770。这样既保证部署用户能管理代码,又避免Web进程获得过大的代码修改权限。
使用ACL解决跨用户协作的权限粒度问题传统的用户-组-其他三层权限模型在很多场景下不够用。比如你希望deploy用户可以读写代码目录,同时希望审计用户audit只读日志,而日志目录又需要logrotate进程写入。单靠chown和chmod很难同时满足。Ubuntu默认支持POSIX ACL,可以通过setfacl命令为特定用户或组设置独立权限。例如给audit用户对/var/log/app目录添加只读权限:
sudo setfacl -m u:audit:rx /var/log/app
查看ACL用getfacl命令。ACL的好处是可以在不改变目录主属组的情况下,为多个不同角色分配不同权限。对于需要临时授权、审计只读访问、多个应用共用同一目录但权限要求不同的场景,ACL比反复修改组结构更安全也更清晰。需要注意的是,ACL权限在rsync、tar备份时可能丢失,需要配合相应参数保留扩展属性。
用umask和默认ACL控制新建文件的初始权限很多权限问题来自新建文件时继承了过于宽松的默认权限。单纯修改/etc/profile中的umask只对交互式登录生效,对systemd服务、cron任务、SSH非交互命令不一定有效。更可靠的方式是结合默认ACL。比如你希望/var/www/site目录下所有新建文件和子目录默认组为www-data,且组权限为读写,可以设置:
sudo setfacl -d -m g:www-data:rwx /var/www/site
这样在该目录下新建的文件会自动继承组www-data的读写权限,而不必每次手动chgrp和chmod。同时建议在/etc/login.defs中将UMASK调整为027,这样新建文件默认640,目录750,减少其他用户读取的机会。对于systemd服务,可以在service单元文件中通过UMask=0027显式指定,避免服务进程创建的文件权限过宽。
SUID、SGID和粘滞位的风险控制SUID和SGID位是权限配置中最容易被忽视也最危险的部分。一个设置了SUID的可执行文件会以文件所有者的身份运行,如果这个文件属于root且存在漏洞,攻击者就能提权。Ubuntu系统中应定期审计SUID/SGID文件:
sudo find / -perm -4000 -type f 2>/dev/null sudo find / -perm -2000 -type f 2>/dev/null
对于不需要SUID的程序,用chmod u-s去除。常见的passwd、sudo等命令需要保留SUID,但很多第三方软件或旧版本工具并不需要。粘滞位主要用于/tmp和/var/tmp这类共享可写目录,确保用户只能删除自己的文件。检查这些目录权限是否为1777,如果不是,用chmod 1777设置。同时建议将/tmp挂载为nosuid、nodev、noexec,进一步降低共享目录被利用的风险。
Web应用目录的权限隔离策略Web应用是权限配置的重灾区。很多教程直接让用户执行chmod -R 777,这是最危险的做法。正确策略是区分代码目录和上传目录。代码目录应只允许部署用户写入,Web进程只读。上传目录、缓存目录、session存储目录需要Web进程写入,但应限制在这些特定子目录内,而不是整个应用目录。以Nginx+PHP-FPM为例,PHP-FPM进程以www-data运行,代码目录设置为deploy:www-data,权限750,上传目录设置为www-data:www-data,权限770。同时可以在PHP-FPM池配置中使用php_admin_value[open_basedir]限制PHP脚本只能访问应用目录和临时目录,防止跨目录读取。
对于需要写入的配置文件,比如.env或config.php,部署时由部署用户写入,Web进程只读,权限640,属主deploy,属组www-data。这样即使Web应用存在文件读取漏洞,攻击者也只能以www-data身份读取,而www-data对代码目录只有读和执行权限,无法修改源码。如果应用需要在线修改配置,应单独设置一个可写配置文件,权限660,属主www-data,属组deploy,并明确该文件的风险。
数据库和备份文件的权限加固数据库备份文件往往包含完整业务数据,但很多自动化备份脚本生成的备份文件权限是644,任何本地用户都能读取。备份脚本中应显式设置umask 077,或者备份完成后用chmod 600收紧权限。备份目录本身建议权限700,属主为执行备份的账户。如果备份需要同步到远程服务器,使用SSH密钥认证并限制密钥权限为600,同时远程备份目录权限700。对于数据库配置文件,比如/etc/mysql/my.cnf或应用中的数据库连接配置,权限应设为600,属主为需要读取的进程账户,避免其他用户看到数据库凭据。
日志文件的权限与轮转安全日志文件常包含敏感信息,如用户IP、请求参数、内部路径等。默认情况下/var/log下很多日志是640,属主root,属组adm或syslog。这意味着adm组的用户可以读取大部分系统日志,这是合理的,但应用日志往往权限混乱。建议应用日志目录权限750,日志文件权限640,属主为应用运行账户,属组为需要查看日志的运维组。如果使用logrotate轮转,在logrotate配置中通过create指令指定新建日志文件的权限和属主,例如:
/var/log/app/*.log {
daily
rotate 14
compress
create 640 appuser opsgroup
}
这样可以避免轮转后日志权限被重置为默认值。对于特别敏感的日志,比如包含认证信息的日志,权限应设为600,仅应用账户和root可读。
systemd服务的权限约束systemd服务默认以root运行,很多服务并不需要root权限。通过service单元文件中的User、Group、UMask、NoNewPrivileges、ProtectSystem、ProtectHome、ReadWritePaths等指令可以大幅降低服务权限。例如一个Go应用服务可以这样配置:
[Service] User=appuser Group=appgroup UMask=0027 NoNewPrivileges=true ProtectSystem=strict ProtectHome=true ReadWritePaths=/var/lib/app /var/log/app
ProtectSystem=strict将整个文件系统设为只读,只有ReadWritePaths指定的目录可写,这样即使服务被入侵,攻击者也无法修改系统文件或安装持久化后门。ProtectHome=true隔离/home目录,防止读取其他用户数据。这些systemd安全指令比单纯依赖文件权限更彻底,因为它们在内核层面限制了进程的访问能力。
定期审计与自动化权限检查权限配置不是一次性工作,系统运行过程中会不断产生新文件、新用户、新服务。建议定期执行权限审计,重点检查:全局可写目录、SUID/SGID文件、无主文件、权限过宽的敏感文件。可以使用find命令配合脚本实现自动化检查:
sudo find / -type d -perm -0002 ! -path "/proc/*" ! -path "/sys/*" 2>/dev/null sudo find / -type f -perm -0002 ! -path "/proc/*" ! -path "/sys/*" 2>/dev/null sudo find / -nouser -o -nogroup 2>/dev/null
将审计脚本加入cron每周执行,结果发送到管理员邮箱。对于关键目录,可以建立权限基线清单,记录每个目录的属主、属组、权限位和ACL,定期对比变化。如果使用配置管理工具如Ansible,可以将权限配置写成playbook,定期执行确保权限不被意外修改。
权限精细化配置的核心原则是最小权限:每个用户、每个进程只获得完成任务所必需的最小权限。这需要从目录规划、默认权限、ACL、服务约束、定期审计多个层面同时推进,而不是简单执行几条chmod命令。Ubuntu提供了足够的工具来实现细粒度控制,关键在于管理员是否愿意在部署初期就投入精力设计权限模型,并建立持续的审计机制。安全不是一次性的配置,而是一个需要不断维护的过程。
