VPN 命令以你自己的用户身份运行,不出现密码,隧道就建立起来了。这一连串过程感觉像是非特权操作,而它通常并不是。真正的网络改动由一个独立的服务完成,有意思的问题是:对未经它验证的调用方,这个服务会做什么。数一数弹了几次密码,对这条边界毫无说明。

接口、机制与检查

接口接受请求:连接、断开、选择配置。机制掌握网络操作:创建接口、修改路由、应用过滤。授权检查决定这个调用方能否请求这个动作。这三者可能分散在一个二进制程序、一个辅助程序和一个系统服务里,而用户只看得见第一个。

这些角色可能住在同一个二进制里,也可能分成两个进程,或者分成一个客户端和一个系统服务,而用户看到的命令很少透露是哪一种。托盘图标和系统守护进程看起来可能像同一个应用,实际运行时权限却大不相同。安装时的权限和请求时的权限也是两回事。以 root 运行的安装程序可能留下一个服务,在包管理器结束很久之后仍在监听后续请求。持有权限的是那个服务,而不是你敲下的命令。

委托是正常的设计

把有特权的网络工作委托给一个服务,是合理的架构。它让普通应用保持非特权,并把需要小心处理的操作集中起来。只有服务同时检查是谁在请求、请求什么,这个设计才划算。没有这项检查,它只是一扇更宽的门。

NetworkManager 是 Linux 上的具体例子。它的配置指南说明了默认设置下 PolicyKit 如何授权非 root 的请求。这说明委托式授权在真实产品中确实存在。它并不能证明每个商业客户端都使用 NetworkManager,也没有描述任何特定厂商的套接字路径或策略。

这个模式是背后有策略的委托。这个例子是该模式的一种实现,而不是普遍做法。

读边界,而不是读提示

找到那个服务、它的配置所有者,以及前端使用的本地接口。然后问:哪些用户能请求哪些操作。能开关一个已保存的配置,和能加载运维人员从未审阅过的配置,不是同一种权限。这个差别就是整个安全问题。

从服务管理器和客户端对自身权限的看法入手。下面的命令会显示守护进程是否在运行、调用方能请求它做什么,以及它在运行时目录下暴露了什么。

systemctl status NetworkManager
nmcli general permissions
ls -l /run/NetworkManager

还要问这项操作会影响什么。看起来只关乎个人的菜单项,可能改动整台机器的路由和 DNS,也就是说它会干扰其他用户的会话。影响范围应当和权限放在同一次审查里,而且通常正是它决定服务是否需要更窄的角色。

用受支持的方式修权限

把受保护的控制套接字改成所有人可写,或者把账号加进一个宽泛的管理组,等于消掉一个权限错误,却造出一个更大的。那个错误是唯一在告诉你这条边界存在的东西。请使用有文档记录的成员资格或授权流程,包括它要求的会话刷新。组成员资格往往只对新登录的会话生效,所以改动在下次登录之前看起来毫无作用。

如果产品为这项操作提供了更窄的角色,优先用它而不是一刀切的授权。把选定的角色和它的所有者写在服务名旁边。

好的划分是什么样子

普通应用保持非特权,服务只做它被设计来做的那部分设置,每个调用方都要按某个有人负责的策略接受检查。这是站得住脚的设计。它也可审计,因为策略是有名字挂在名下的。任何比这更大的东西都该有一份书面例外,而不是悄悄地改权限。

失败的样子是:一个服务因为接口方便,就执行任何本地用户的任何请求。从用户一侧看毫无区别,而它携带的权限要大得多。要测一个被拒绝的请求,而不只是成功的那个。来自策略的拒绝,才是策略存在的证据。

相关文章