CentOS服务器上跑着大量生产应用,日志文件往往记录了接口请求参数、数据库连接信息、用户会话令牌甚至内部网络拓扑。默认安装的rsyslog、Nginx、Tomcat、MySQL等组件生成的日志权限经常是644或640,这意味着同组其他用户甚至所有登录用户都能直接读取。一旦低权限账号被攻破,攻击者不需要提权就能从日志里翻出敏感字段,这是很多运维团队忽略的横向渗透入口。解决这个问题的核心不是简单chmod一把梭,而是要把日志从产生、轮转、归档到清理的整个生命周期都纳入权限管控。
先摸清当前日志权限的真实分布不要凭印象判断,直接跑一条命令把关键日志目录的权限全部列出来。常见的日志路径包括/var/log/messages、/var/log/secure、/var/log/nginx/、/var/log/mysql/、/var/log/audit/以及各应用自己指定的目录。用stat命令查看具体文件的权限位和属主属组,比ls -l更详细。很多管理员发现/var/log/nginx/下面的access.log是644,属主root,属组root,但Nginx的worker进程是以nginx用户运行的,写日志靠的是打开文件时的文件描述符,所以能写,但其他普通用户只要登录了系统就能读。这种读写分离的权限模型本身就是风险。建议把日志目录统一设置为750,日志文件设置为640,属主保持root或应用专用账号,属组归入一个专门的logread组,只有运维审计账号才加入这个组。
应用层日志生成时的权限控制Nginx和Apache这类Web服务器在启动时由root进程打开日志文件,之后子进程继承文件描述符写入。所以日志文件的初始权限由root创建时的umask决定。CentOS默认umask是022,新建文件就是644。可以在Nginx的systemd unit文件里加入UMask=0027,这样Nginx创建的日志文件权限自动变成640。修改/etc/systemd/system/nginx.service.d/override.conf,加入下面内容:
[Service] UMask=0027
然后执行systemctl daemon-reload和systemctl restart nginx。对于Tomcat,catalina.out的权限由启动脚本的umask控制,可以在catalina.sh开头加上umask 027。Java应用内部使用log4j或logback写日志时,如果日志文件由应用进程创建,权限也受进程umask影响。更稳妥的做法是在应用启动前显式设置umask,比如在启动脚本里加上umask 027再exec java。MySQL的错误日志和慢查询日志默认权限一般是640,但要注意MySQL 5.7之后如果开启log_syslog,日志会交给rsyslog处理,权限由rsyslog的umask决定。
rsyslog与系统日志的权限加固CentOS 7和8默认使用rsyslog,/etc/rsyslog.conf里可以针对不同facility指定日志文件权限。全局umask可以在rsyslog.conf顶部加入$umask 0027,但这个指令只影响rsyslog新建的文件。更细粒度的控制可以用$FileCreateMode指令,比如:
$umask 0027 $FileCreateMode 0640
这两条指令放在rsyslog.conf的MODULES部分之前。修改后重启rsyslog服务。对于/var/log/secure这种认证日志,必须确保只有root和审计组可读,因为里面记录了sudo命令、ssh登录尝试、甚至密码失败时的用户名。很多安全基线要求secure日志权限为600,这个可以单独设置。auditd的日志/var/log/audit/audit.log默认权限就是600,但轮转后生成的audit.log.1可能变成600或640,要检查/etc/audit/auditd.conf里的log_group配置,把log_group设置为root可以保持严格权限。
日志轮转环节最容易出现权限回退logrotate是CentOS上最常用的日志轮转工具,它的配置文件/etc/logrotate.conf和/etc/logrotate.d/目录下的各应用配置决定了轮转后新日志文件的权限。很多人在logrotate配置里只写了compress、daily、rotate 7,没有写create的权限参数,导致轮转后新日志文件权限回到默认644。正确写法是在每个日志轮转配置块里显式指定create模式:
/var/log/nginx/*.log {
daily
rotate 14
compress
delaycompress
missingok
notifempty
create 0640 nginx logread
sharedscripts
postrotate
/bin/kill -USR1 $(cat /var/run/nginx.pid 2>/dev/null) 2>/dev/null || true
endscript
}
上面的create 0640 nginx logread表示轮转后新建的日志文件权限为640,属主nginx,属组logread。注意属组必须已经存在,否则logrotate会报错。对于MySQL日志,如果使用mysqladmin flush-logs触发轮转,权限由MySQL进程的umask控制,需要在MySQL的systemd unit里也设置UMask=0027。对于auditd日志,它使用自己的轮转机制,配置文件在/etc/audit/auditd.conf,参数max_log_file_action默认是ROTATE,轮转后的文件权限继承原文件,一般不会出问题,但要定期检查。
集中日志采集场景下的权限设计很多企业会把CentOS服务器上的日志采集到集中日志平台,比如ELK、Loki或商业SIEM。采集agent通常以root或专用账号运行,读取本地日志文件。如果日志权限设置过严,agent读不到;设置过松,又暴露给其他进程。建议为日志采集agent单独创建一个账号,比如logagent,把这个账号加入logread组,日志文件和目录的属组统一为logread,权限750/640。这样agent能读,其他普通用户不能读。如果使用Filebeat,它的配置文件filebeat.yml里可以指定日志路径,运行账号在systemd unit里设置User=logagent。注意Filebeat读取日志后如果做了多行合并或者解析,它自己产生的registry文件也要保护,里面可能包含文件路径和偏移量,泄露后能推断出日志结构。
防止日志内容本身泄露敏感字段权限管控只能防止文件被未授权读取,但日志内容如果明文记录了密码、token、身份证号、手机号,即使权限正确,一旦日志被合法读取或转发到集中平台,泄露风险依然存在。应用层要做日志脱敏,在log4j2的pattern里使用%replace或自定义converter把敏感字段打码。Nginx的access log默认记录请求URL,如果URL里带query string,可能包含token或密码参数。可以在nginx.conf里使用自定义log_format,只记录uri而不记录args,或者用map指令对特定参数脱敏。例如:
log_format safe '$remote_addr - $remote_user [$time_local] "$request_method $uri" $status $body_bytes_sent';
这样就不记录query string了。对于POST请求体,Nginx默认不记录,但有些应用会把请求参数打到业务日志里,这需要开发配合改造。MySQL的general log和slow query log会记录完整SQL语句,如果SQL里包含明文密码或敏感数据,要关闭general log,慢查询日志只记录查询结构而不记录具体值,可以通过设置log_slow_verbosity或使用Percona的慢查询扩展来控制。
利用ACL实现更细粒度的访问控制标准Linux权限只有属主、属组、其他三个级别,有些场景下需要给特定用户读日志权限,但又不想把该用户加入logread组。CentOS支持POSIX ACL,可以对单个文件或目录设置额外访问规则。比如要让监控账号monitor读取/var/log/nginx/access.log,但不让它读同目录下的error.log,可以执行:
setfacl -m u:monitor:r /var/log/nginx/access.log
查看ACL用getfacl。注意logrotate轮转后新建的文件不会自动继承旧文件的ACL,需要在logrotate配置里用aclexec或postrotate脚本重新设置ACL。更简单的做法是对整个目录设置默认ACL,这样新建文件自动继承:
setfacl -d -m u:monitor:r /var/log/nginx/
但默认ACL只对新建文件生效,logrotate轮转时如果使用create方式新建文件,会继承目录默认ACL;如果使用copytruncate方式,文件inode不变,ACL也保留。所以建议日志轮转优先使用create方式,配合目录默认ACL来维持权限一致性。
审计与监控日志访问行为权限设置得再严密,也要监控谁在访问日志文件。CentOS上的auditd可以配置对日志目录的读操作进行审计。在/etc/audit/rules.d/目录下新建规则文件,加入:
-w /var/log/nginx/ -p r -k log_access -w /var/log/mysql/ -p r -k log_access -w /var/log/secure -p r -k log_access
然后重启auditd。这样任何对日志文件的读取操作都会记录到/var/log/audit/audit.log,可以用ausearch -k log_access查询。注意audit日志本身也会增长,要设置合理的轮转和保留策略。另外可以用inotify或fanotify监控日志文件被非授权进程打开的行为,但auditd已经足够覆盖大部分需求。
容器化与云环境下的日志权限延伸CentOS服务器上如果跑了Docker,容器日志默认放在/var/lib/docker/containers/下,每个容器的json日志文件权限是640,属主root,属组root。但Docker的daemon进程以root运行,容器内进程写日志通过stdout/stderr,由Docker采集后写入宿主机文件。这些日志文件同样可能包含敏感信息,要纳入权限管控范围。建议把Docker的日志驱动改为json-file并配合logrotate,在/etc/docker/daemon.json里配置:
{
"log-driver": "json-file",
"log-opts": {
"max-size": "10m",
"max-file": "3"
}
}
同时把/var/lib/docker/containers/目录权限收紧为750,属组归入logread。如果使用Kubernetes,Pod日志通过kubelet写入/var/log/containers/和/var/log/pods/,这些目录的权限同样需要检查。云服务器上的安全组和IAM策略不能替代操作系统层面的文件权限,两者要叠加使用。
定期核查与自动化合规检查权限配置不是一次性的,应用升级、新部署、配置变更都可能引入新的日志文件或目录。建议写一个脚本定期扫描关键日志路径,检查权限是否偏离基线。脚本逻辑可以很简单:遍历指定目录,检查文件权限是否大于640,目录权限是否大于750,属组是否在允许列表中。发现异常直接发告警。把脚本加入crontab每小时执行一次。对于使用配置管理工具的环境,比如Ansible或Puppet,可以把日志权限基线写成代码,定期强制执行,防止手工修改被覆盖。安全基线可以参考CIS CentOS Linux 7/8 Benchmark中关于日志文件权限的章节,里面明确要求/var/log/messages、/var/log/secure等文件权限不高于640,/var/log/audit/audit.log不高于600。
日志权限管控的难点在于日志来源分散、格式多样、轮转机制复杂,而且日志内容本身可能包含敏感数据。单纯靠chmod解决不了内容泄露问题,单纯靠脱敏解决不了文件被读取的问题。必须把文件权限、轮转策略、采集架构、内容脱敏、审计监控五个环节串起来,形成闭环。CentOS作为服务器操作系统,其日志体系相对成熟,但默认配置往往偏向可用性而非安全性,运维人员需要根据实际业务场景做针对性加固。每次上线新应用或调整日志采集方案时,把权限核查作为上线检查项之一,比事后补救成本低得多。
