为运行dotnet watch的用户提供Linux CAP_NET_RAW能力
我有一个应用,最初在Windows上开发,使用ICMP并对TTL值进行自定义以进行跟踪路由。我想从Windows转到Linux,但遇到了一个问题。
在Linux上使用dotnet watch进行开发时,setcap的标志不会被dotnet继承。 (很抱歉,这个术语在Windows的权限中更常用,但权限的继承在Windows权限中也相当重要)
我想做的是让我的用户有权运行时拥有CAP_NET_RAW权限。这样就不需要把dotnet以 sudo运行,在我看来这是一个不太好的选项。网上的大多数建议都不适用,因为它是针对最终部署的可执行文件,或需要在构建阶段对进程/可执行文件设置标志,而那样做仍然需要sudo。
有没有办法仅让我的用户获得NET_RAW权限,而不必使用sudo?
这看起来如果做不到就不太安全,因为现在我被迫在输出的可执行文件上使用一个标志,而我其实只需要一个标志。
我有哪些选项?
解决方案
能力,按照“POSIX 1e”的设想,并不是父进程“直接给”子进程就能完成的。相反,你必须先给子可执行文件一个可继承的文件能力,父进程再提升一个进程可继承能力来传递某些东西。
这样,父进程和子进程都知道自己在做什么,也不会继承意外的特权。这在Linux下与您描述的“天真继承”权限模型有本质的不同。你可以通过 capsh 工具看到这是如何工作的:
$ cp /sbin/capsh ./capsh-parent
$ cp /sbin/capsh ./capsh-child
$ /sbin/getcap -v capsh-*
capsh-child
capsh-parent
$ sudo setcap cap_net_raw=p ./capsh-parent
$ sudo setcap cap_net_raw=i ./capsh-child
$ /sbin/getcap -v capsh-*
capsh-child cap_net_raw=i
capsh-parent cap_net_raw=p
按这种方式设置后,我们可以看到工作原理如下:
首先 ./capsh-child 在没有父进程帮助的情况下看不到特权:
$ ./capsh-child --current
Current: =
Current IAB:
$ ./capsh-parent --current -- -c "echo child ; ./capsh-child --current"
Current: cap_net_raw=p
Current IAB:
child
Current: =
Current IAB:
但如果父进程通过其进程可继承能力来传递它,那么子进程在其被许可集合中就会拥有它:
$ ./capsh-parent --inh=cap_net_raw --current -- -c "echo child ; ./capsh-child --current"
Current: cap_net_raw=ip
Current IAB: cap_net_raw
child
Current: cap_net_raw=ip
Current IAB: cap_net_raw
这就是它“应该”运作的方式。只有特定的二进制才可以以特权运行。要让你的父进程实现这一点,你需要的函数是 cap_set_flag() 和 cap_set_proc()。
话虽如此,你也可以通过Ambient能力向量实现对特权的天真继承:
$ cp /sbin/capsh capsh-child
$ sudo setcap cap_net_raw,cap_setpcap=p ./capsh-parent
$ /sbin/getcap -v capsh-*
capsh-child
capsh-parent cap_setpcap,cap_net_raw=p
$ ./capsh-parent --iab='^cap_net_raw' --current -- -c "echo child ; ./capsh-child --current"
Current: cap_net_raw=ip cap_setpcap+p
Current IAB: ^cap_net_raw
child
Current: cap_net_raw=eip
Current IAB: ^cap_net_raw
这与“POSIX”所期望的方式之间的主要区别在于,在后者的情况下,“echo child”会以特权运行,就像任何一个攻击者可能让父进程替换执行的二进制一样,替代对象是 ./capsh-child。在前者的情况下,只有具备文件可继承能力的二进制才能够获得该特权。
顺便说一句,我在 [libcap 网站关于能力继承] 写了一篇关于能力继承的长文。