如果一台笔记本的杀毒签名几周前就停止更新,却仍能打开你的内部应用,那么问题通常不出在策略文档上。拒绝访问之前,三个独立的系统必须达成一致:有东西测量端点,有东西把测量结果变成允许或拒绝的判定,还有东西真正丢弃流量。当不合规的设备依然进入了内网,恰恰是其中一环缺失,或者它相信了本不该相信的证据。下面的检查按这个顺序进行,每一步都有一个停下来的位置。

拦截是最后一步,而不是第一步

认证和状态回答的是不同的问题。登录成功只证明出示了凭据,对出示凭据的那台机器上运行着什么一无所知。正是这个缺口,让复制来的客户端配置能把一个看起来健康的会话带到不健康的主机上。

端点合规的工作已经有了描述这条链的词汇。RFC 5209 中的 NEA 参考模型把工作拆成采集器、评估步骤和执行点,并明确指出:对自己的状态撒谎的客户端不在模型之内。这个提醒比图更重要——可信平台模块并不会把自我上报的测量变成事实。

让采集器对范围保持诚实。它应当报告策略点名的那几个属性,落在请求访问的设备上,其余少报为宜。把每个已安装包都列一遍的资产代理,与状态采集器是两种工具,而差别同时是留存问题和准确性问题。如果报告无法绑定到单一设备身份,评估者就是在猜他刚刚放行了哪台机器。

显示红色状态的仪表盘不是控制。要问被拒绝的用户还能到达什么:wiki、文件共享、数据库端口?补救只需要一个窄口子,其余一切都应该在拦截之后。

先读报告,再读规则

从源头开始,而不是从客户端那个绿勾开始。在 Windows 上,本地测量来自 Defender,或来自向 Security Center 注册的产品,两者都能不依赖厂商控制台读取:

Get-MpComputerStatus | Select-Object AMServiceEnabled, RealTimeProtectionEnabled, AntivirusSignatureLastUpdated
Get-CimInstance -Namespace root/SecurityCenter2 -ClassName AntiVirusProduct | Select-Object displayName, productState

第二条命令返回一个名为 productState 的整数。它的位定义并不严谨,各家厂商打包启用位与时效位的方式也不一样,所以要交叉验证而不是直接解析。第三方套件在卸载后还会留下 Security Center 条目,而这条目报告的是已经不在磁盘上的软件。

人们最常跳过的是年龄字段。签名时间戳只有相对于做评估那台机器的时钟才有意义,所以要把它换算成时间间隔,而不是目测:

$sig = (Get-MpComputerStatus).AntivirusSignatureLastUpdated
[int](New-TimeSpan -Start $sig -End (Get-Date)).TotalHours

现在策略有了一个可读的数字。如果你的规则写的是 72 小时,它用的就是这个数字,睡了一周的笔记本会在没人解释标签的情况下失败。把阈值写在命令旁边,因为端点工具一换,两者就会脱节。

把每种设备状态变成书面判定

关于状态的争议,多数其实是关于“未知行”的争议。在第一次事件之前就把它定下来,用一张表写清证据、判定和负责人,这样没人会在网关控制台上临时拍板。

Evidence the assessor hasVerdictWhat it protects against
Timestamp inside the policy window, protective service runningAdmit to normal applicationsNothing yet; the other checks still apply
Timestamp older than the windowDeny, admit remediation page onlySignature rollback and dormant laptops
Service disabled or reporting offDeny, escalate to the endpoint ownerAn agent reporting stale success
No report at all for this sessionTreat as deny, never as healthyAgents that fail open under load

产品差异恰恰体现在最下面一行。有的网关直接拒绝连接并说明原因;有的让隧道建立却套用受限策略;少数会回退到最后一次已知良好的报告。只有前两种站得住脚,值得测一下你的是哪一种。

身份和权限也要各走各的道。通过所有状态测试的笔记本,仍然需要针对应用的正确人工授权;设备证书证明的是持有密钥,而不是文件系统的状态。把三者压成一个布尔值,正是服务台账号拿到生产环境常驻权限的路径。

用请求验证执行路径,而不是仪表盘

执行是请求的属性,所以用一个请求去测量。把不合规的机器放进隧道,发出真实用户会发的调用:

curl -sS -o /dev/null -w '%{http_code}
' --max-time 5 https://app.internal.example/health

本该被拦截的设备返回 200,说明判定从未到达数据路径。常见原因有两个:策略绑定在测试账号从未加入的组上,或者应用能通过 VPN 不拥有的路由到达。

先检查路由假设。如果内部域名解析到的地址,客户端经本地 LAN 已经能到,那么隧道无关紧要,也不会有任何策略生效。把这个地址和网关推送的路由比一比。在 OpenVPN 上,按客户端的策略放在以通用名为键的文件里,所以很容易查看:

ls /etc/openvpn/ccd/
cat /etc/openvpn/ccd/laptop-17

含有 disable 的文件会直接断开该客户端,而 push "route ..." 那一行则收窄它能到达的范围。两者都是执行,也都对调试报告的人可见。

在一个测试账号上测四种情况

最后的检查是一个小矩阵,而不是一条顺风路。让一个测试账号走四种状态:最新、签名过期、防护关闭、代理未安装。每一种都记录判定到评估日志、应用返回的 HTTP 状态,以及补救页面是否加载。

foreach ($case in 'current', 'stale', 'disabled', 'absent') {
  Get-MpComputerStatus | Select-Object AMServiceEnabled, AntivirusSignatureLastUpdated
  curl.exe -sS -o NUL -w "%{http_code}
" --max-time 5 https://app.internal.example/health
}

两个细节容易被忽略。先看重放:在机器健康时抓取的报告,不能被同一设备的后续会话接受,所以要找与当前连接绑定的时间戳。再看移除:卸载代理应当产生与过期报告相同的拒绝,而不是干净放行,而这正是最常被漏测的情况。

当矩阵和表格对上,交给下一位运行网关的人三样东西:命令、阈值和四个预期结果。这次交接才是真正的交付物,因为只活在策略文档里的状态设计,会在端点工具第一次改动上报字段时走样。

相关文章