香港CN2线路不稳定排查时,如何检查监控日志权限与服务身份

当访问香港CN2线路上的服务出现间歇性超时、连接时好时坏,而监控面板又存在数据空洞、日志延迟或告警时,问题不一定发生在线路本身。监控进程无法读取目标日志、无法写入状态目录、轮转后失去新日志权限,或者服务实际运行用户与预期不一致,都可能把“观测中断”误判为“线路不稳定”。
排查“香港CN2线路不稳定是什么原因,该怎么排查”时,建议按照低风险到高风险的顺序进行:先用独立探测确认故障是否真实存在,再核对监控服务身份和日志来源,随后逐级检查用户、用户组、目录遍历权限、文件读写权限、日志轮转以及systemd安全限制,最后才进行最小范围的权限修复。每次修改后,都要以监控用户身份复测,不能只用root查看结果。
先确认:线路波动还是监控观测中断
权限问题通常不会直接造成网络链路丢包,但会造成以下几类假象:
- 监控服务本身仍显示运行中,但无法读取目标日志,导致图表停止更新。
- 监控日志只记录到某个时间点,之后出现空白,面板可能被解释为超时或无响应。
- 日志轮转后,新文件由不同用户或用户组创建,监控服务突然失去读取权限。
- 服务可以启动,却无法写入状态目录、临时目录或检查点文件,随后反复重启。
- root查看日志正常,但以监控服务身份执行相同操作失败。
先记录故障发生时间、目标地址、监控服务名称以及监控日志最后一条记录的时间。以下命令适用于使用systemd的Linux服务器,命令本身为只读检查:
date -Is
systemctl list-units --type=service --state=running --no-pager
如果服务提供可访问的业务端口,可以在服务器或授权的探测主机上进行低频、受控的连接测试:
ping -c 20 -i 0.2 <目标地址>
如果目标是HTTP服务,也可以使用:
curl --connect-timeout 5 --max-time 10 -sS -o /dev/null \
-w 'time_connect=%{time_connect} time_starttransfer=%{time_starttransfer} http_code=%{http_code}\n' \
https://<目标域名或地址>/
ping存在丢包而业务请求正常时,不能直接判定线路故障,因为ICMP和业务流量的处理方式可能不同。反过来,如果业务请求确实超时,但监控日志在同一时刻停止更新,应先处理监控权限和服务身份,避免把日志缺失当成网络证据。
可以用下面的结果快速分支:
| 观察结果 | 更可能的含义 | 下一步 |
|---|---|---|
| 独立请求异常,监控日志持续写入 | 故障可能真实存在,监控权限不是首要原因 | 保留监控证据,转入线路和目标服务的独立排查 |
| 独立请求正常,监控日志停止更新 | 监控进程、日志读取或状态写入存在问题 | 检查服务身份和文件权限 |
| root能读日志,监控用户不能读 | 用户、用户组、ACL或目录遍历权限不匹配 | 按路径逐级检查权限 |
| 当前日志可读,轮转后的日志不可读 | 日志轮转创建文件时的属主、属组或模式不正确 | 检查轮转规则和新旧文件权限 |
| 文件权限看起来正常,但服务仍报拒绝访问 | systemd沙箱、SELinux或AppArmor等额外控制生效 | 查看拒绝记录和服务限制 |
第一步:确认监控服务到底使用哪个身份
不要根据配置文件中的用户名猜测运行身份。systemd服务可能通过User=、Group=、SupplementaryGroups=、DynamicUser=或启动脚本改变实际身份,主进程和子进程也可能不同。
先定义需要检查的服务单元名称。下面的<监控服务>.service必须替换为实际服务名:
UNIT="<监控服务>.service"
sudo systemctl status "$UNIT" --no-pager
sudo systemctl show "$UNIT" \
-p User \
-p Group \
-p SupplementaryGroups \
-p DynamicUser \
-p MainPID \
-p ExecStart \
-p StandardOutput \
-p StandardError
重点看以下字段:
User=:主服务进程的用户。Group=:主服务进程的主用户组。SupplementaryGroups=:额外用户组,读取日志时经常依赖该字段。DynamicUser=yes:服务使用临时身份,不能简单按照固定用户名授权。MainPID=:当前主进程编号。StandardOutput=和StandardError=:判断日志究竟写入journald、文件还是其他位置。
如果User=为空,通常表示服务使用默认身份运行,常见情况下是root,但仍应结合服务文件和进程结果确认,不要直接据此修改权限。继续检查主进程及其子进程:
PID="$(sudo systemctl show "$UNIT" -p MainPID --value)"
sudo ps -o pid,ppid,user,group,egroup,args -p "$PID"
sudo systemctl cat "$UNIT"
如果服务启动了多个工作进程,可以查看同一服务相关进程:
sudo ps -eo pid,ppid,user,group,egroup,args --forest
若看到监控程序使用的是monitor,后续所有读取测试都应以monitor身份执行;如果实际身份是临时用户,则应先确认systemd为该服务分配的运行环境,再决定授权方式。不要为了方便把日志目录改成所有用户可读,也不要把服务改为root运行作为临时修复。
第二步:确定日志来源和实际路径
监控日志可能来自三种位置:
- systemd journal;
2.应用或监控程序写入的普通日志文件; 3.服务运行时写入的状态、缓存或检查点目录。
先以管理员身份查看服务近期输出,再用监控用户执行同样的读取操作,比较二者结果:
sudo journalctl -u "$UNIT" --since "30 min ago" --no-pager
sudo -u monitor -- journalctl -u "$UNIT" --since "30 min ago" --no-pager
这里的monitor只是示例,必须替换为上一步确认的实际用户。如果第一条命令有日志、第二条命令提示权限不足或没有任何记录,问题很可能是journal读取权限,而不是日志内容本身消失。
如果日志写入普通文件,需要从服务配置、启动参数或程序配置中确认完整路径。不要只检查文件本身,还要检查路径上的每一级目录。例如目标文件是:
/var/log/example/monitor.log
需要同时检查/var、/var/log、/var/log/example和monitor.log。Linux读取文件要求:
- 对文件本身具备
r权限; - 对文件所在目录具备
x权限; - 如果要列出目录内容,还需要目录的
r权限; - 如果要创建、重命名或删除日志文件,需要目录同时具备
w和x权限。
第三步:逐级检查用户、用户组和目录权限
先查看用户和组是否存在,以及服务实际拥有的附加组:
id monitor
getent passwd monitor
getent group
随后查看路径上每一级目录和文件的权限:
namei -l /var/log/example/monitor.log
stat -c '%A %a %U %G %n' \
/var \
/var/log \
/var/log/example \
/var/log/example/monitor.log
如果系统没有namei,可以分别执行:
ls -ld /var /var/log /var/log/example
ls -l /var/log/example/monitor.log
更重要的是,以监控服务身份进行实际测试。以下命令不会修改文件:
sudo -u monitor -- test -r /var/log/example/monitor.log \
&& echo "file-readable" \
|| echo "file-not-readable"
sudo -u monitor -- test -x /var \
&& echo "/var-traversable" \
|| echo "/var-not-traversable"
sudo -u monitor -- test -x /var/log \
&& echo "/var/log-traversable" \
|| echo "/var/log-not-traversable"
sudo -u monitor -- test -x /var/log/example \
&& echo "app-log-dir-traversable" \
|| echo "app-log-dir-not-traversable"
如果监控程序还需要写入状态文件,应检查目录而不是只检查一个不存在的文件:
sudo -u monitor -- test -w /var/lib/example \
&& echo "state-dir-writable" \
|| echo "state-dir-not-writable"
sudo -u monitor -- test -x /var/lib/example \
&& echo "state-dir-traversable" \
|| echo "state-dir-not-traversable"
结果含义如下:
file-not-readable:文件属主、属组、模式或ACL不允许该用户读取。app-log-dir-not-traversable:即使文件显示为可读,监控用户也无法进入父目录,实际仍然读不到。state-dir-not-writable:监控服务可能无法保存状态、游标或检查点,容易出现重复告警、数据中断或重启。- root测试成功、监控用户测试失败:优先修复身份和权限,不要据此判断线路正常或异常。
-所有测试成功但应用仍报权限错误:继续检查systemd沙箱和安全策略。
如果文件使用了ACL,仅查看传统的ls -l可能不够,需要执行:
getfacl -p /var/log/example
getfacl -p /var/log/example/monitor.log
输出中的mask::会影响用户组和命名用户ACL的最终有效权限。即使看到user:monitor:r--,如果有效掩码不允许读取,实际测试仍可能失败。
第四步:检查日志轮转和新文件权限
“当前日志可读、过一段时间又失效”是权限排查中很常见的分支。原因通常不是原文件突然变化,而是日志轮转后生成了新的文件,新文件的属主、属组或模式不同。
先列出同一目录下的当前日志和历史日志:
ls -la /var/log/example
stat -c '%A %a %U %G %n' /var/log/example/*
再检查轮转配置,但不要直接执行会产生轮转动作的命令:
sudo grep -R "/var/log/example" /etc/logrotate.conf /etc/logrotate.d 2>/dev/null
sudo logrotate -d /etc/logrotate.conf
logrotate -d用于调试和查看计划,通常不会执行实际轮转。重点核对:
- 新建日志文件的属主和属组;
- 新建日志文件的模式是否包含监控用户所需的读取权限;
- 轮转后目录是否仍允许监控用户遍历;
- 应用是否重新打开了新日志;
- 监控程序读取的是固定路径、软链接,还是已打开的文件描述符。
如果服务使用systemd journal,则不应把普通文件权限修复方法套用到journal上,应单独检查监控服务是否有读取journal的权限。可以先确认系统是否存在相关用户组:
getent group systemd-journal
将监控服务加入能够读取系统journal的组,可能扩大其可见日志范围。只有在确有必要且符合权限策略时才采用,优先考虑让监控程序读取专用日志文件或经过明确授权的日志来源。
第五步:排除systemd沙箱和安全策略限制
当传统权限测试全部通过,但服务日志仍出现Permission denied、Read-only file system或无法创建状态文件时,可能是systemd对服务设置了额外限制。
检查相关属性:
sudo systemctl show "$UNIT" \
-p ProtectSystem \
-p ReadOnlyPaths \
-p ReadWritePaths \
-p InaccessiblePaths \
-p PrivateTmp \
-p NoNewPrivileges \
-p SupplementaryGroups
常见判断方式:
ProtectSystem限制了服务对系统目录的写入;ReadOnlyPaths将某些路径设为只读;ReadWritePaths只允许服务写入明确列出的目录;InaccessiblePaths使路径对服务不可见;SupplementaryGroups没有包含日志读取所需的组;PrivateTmp=yes使服务看到的是独立临时目录,不能假设它能访问主机上的临时文件。
如果系统启用了SELinux或AppArmor,还要查看是否存在拒绝记录:
getenforce 2>/dev/null || true
sudo ausearch -m AVC -ts recent 2>/dev/null
sudo journalctl -k --since "30 min ago" --no-pager \
| grep -Ei 'apparmor|denied|audit'
这类限制不能通过盲目增加chmod权限解决。尤其不要为了验证而关闭SELinux、AppArmor或systemd保护。应根据拒绝记录确认具体路径,再修改正确的安全策略或服务白名单。
第六步:采用最小权限方式修复
修复前先保存当前状态,避免修改后无法恢复。以下命令只用于备份权限信息:
sudo getfacl -p /var/log/example > /root/example-dir.acl.before
sudo getfacl -p /var/log/example/monitor.log > /root/monitor.log.acl.before
sudo systemctl cat "$UNIT" > /root/"$UNIT".before
仅需读取单个日志文件时
如果监控用户只需要读取某一个日志文件,可以使用ACL授予精确权限,而不是把目录改成对所有用户开放:
sudo setfacl -m u:monitor:r /var/log/example/monitor.log
sudo -u monitor -- test -r /var/log/example/monitor.log \
&& echo "read-test-passed" \
|| echo "read-test-failed"
如果父目录缺少遍历权限,还需要对必要的目录授予x权限。但必须先用namei -l确认是哪一级缺失,不要对整个/var或/var/log进行递归放宽。
该修改的影响范围是指定用户和指定文件。回滚时使用之前保存的ACL:
sudo setfacl --restore=/root/monitor.log.acl.before
如果日志轮转后仍然失效,说明只修复当前文件不够,应在轮转规则中固定新文件的属主、属组和读取模式,再通过测试轮转后的新文件进行验证。
需要读取同一目录下多个日志时
如果监控服务确实需要读取某个专用日志目录,可以考虑使用专用用户组。但要确认该组不会让监控用户看到无关的敏感文件。修改前记录目录原属主、属组和模式:
stat -c '%A %a %U %G %n' /var/log/example
getfacl -p /var/log/example > /root/example-dir.before
如果已有合适的日志读取组,应只将监控用户加入该组,并通过轮转规则让新日志继承正确的组和读取模式。用户组变更通常需要重启监控服务或重新建立登录会话后才会生效:
sudo usermod -aG <已确认的日志读取组> monitor
sudo systemctl restart "$UNIT"
该操作会影响监控服务的组成员关系,并可能造成短暂采集间隔。执行前应确认服务允许重启,且已经保存当前配置。回滚时移除新增的附加组,并再次重启服务:
sudo gpasswd -d monitor <已确认的日志读取组>
sudo systemctl restart "$UNIT"
不要直接使用递归chmod -R 777或把服务改为root。前者会扩大所有用户的访问范围,后者会扩大服务进程受到入侵时的影响面,也无法解决轮转规则和安全策略不匹配的问题。
服务只能写入指定状态目录时
如果systemd启用了只读保护,正确做法通常是把实际需要写入的目录加入允许列表,而不是关闭整体保护。修改前保存服务单元内容,并确认目录用途仅限于状态或检查点:
sudo systemctl cat "$UNIT" > /root/"$UNIT".before
sudo ls -ld /var/lib/example
编辑专用的systemd覆盖配置:
sudo systemctl edit "$UNIT"
加入类似内容,路径必须替换为实际状态目录:
[Service]
ReadWritePaths=/var/lib/example
保存后重新加载配置:
sudo systemctl daemon-reload
sudo systemctl restart "$UNIT"
该修改会改变服务可写路径,影响范围是指定目录。不要随意添加根目录、整个/var或其他包含敏感数据的路径。如果确认覆盖配置有误,且该覆盖文件就是本次新增的,可以回滚:
sudo systemctl revert "$UNIT"
sudo systemctl daemon-reload
sudo systemctl restart "$UNIT"
执行systemctl revert前必须确认该服务没有其他需要保留的覆盖配置,否则应手动删除本次新增的配置片段,而不是一次性移除全部覆盖文件。
修复后的验证方法
权限修复不能只看systemctl status显示active。至少要完成以下验证。
以服务身份重新读取和写入测试
读取日志:
sudo -u monitor -- test -r /var/log/example/monitor.log \
&& echo "log-readable" \
|| echo "log-not-readable"
如果服务需要写入状态目录,可以在非生产目录执行临时写入测试。不要直接在正式状态目录创建未知文件;更稳妥的方式是使用程序自身的检查模式,或在维护窗口观察其是否能生成状态文件。
对比管理员和服务用户看到的日志
sudo journalctl -u "$UNIT" --since "10 min ago" --no-pager
sudo -u monitor -- journalctl -u "$UNIT" --since "10 min ago" --no-pager
两者应在权限允许的范围内得到一致的目标服务记录。如果管理员能看到新日志,而服务用户仍看不到,说明权限修复没有覆盖实际日志来源。
检查服务身份是否保持预期
sudo systemctl show "$UNIT" \
-p User \
-p Group \
-p SupplementaryGroups \
-p MainPID
PID="$(sudo systemctl show "$UNIT" -p MainPID --value)"
sudo ps -o pid,ppid,user,group,egroup,args -p "$PID"
重启后身份变化,通常意味着配置文件、动态用户或启动方式仍未统一。应以重启后的实际进程身份为准重新检查,不要沿用重启前的判断。
验证日志轮转后的权限
先进行配置调试,不要在业务高峰期强制轮转:
sudo logrotate -d /etc/logrotate.conf
在经过批准的维护窗口完成轮转后,再查看新文件:
ls -la /var/log/example
sudo -u monitor -- test -r /var/log/example/monitor.log \
&& echo "new-log-readable" \
|| echo "new-log-not-readable"
如果新文件再次不可读,应修复轮转创建规则,而不是反复对当前文件执行setfacl。
复测与持续观察
完成权限修复后,重新执行独立连接测试,同时观察监控日志、服务日志和状态文件的时间戳是否连续。判断修复是否有效,至少要满足以下条件:
- 监控用户可以读取实际日志来源,而不只是读取某个旧文件;
- 服务重启后,运行用户、主用户组和附加组仍符合预期;
- 日志轮转后,新文件仍具备必要的读取权限;
- 状态目录可写,但服务没有因此获得无关目录的写权限;
- 监控日志不再出现权限拒绝、无法打开文件或状态保存失败;
- 独立探测与监控记录的异常时间能够对应起来。
如果独立探测持续超时或丢失,而监控服务能够连续写入日志、读取目标文件、完成轮转并正常保存状态,那么权限问题就不是主要原因。此时应保留已确认的监控证据,避免继续扩大文件或服务权限,再转入线路路径、目标服务和连接协议的独立排查。