译序
学习SELinux有一段时间了,阅读过一些文档,也进行过一些实际操作。但是对于一些概念理解的还是不太深刻,偶有机会得遇如此佳篇,顿有醍醐灌顶茅塞顿开之感。遂译之。
不曾读过SELinux相关的中文权威文档,所以对一些名词、术语的翻译可能有些生硬。领会精神吧。
在此表示对原作者Daniel J Walsh的感谢,原文地址:https://opensource.com/business/13/11/selinux-policy-guide
正文
SELinux是一个标签系统。每个进程都有一个标签。操作系统中的每个文件对象/目录对象也都有一个标签。甚至连网络端口,设备和潜在的主机名也都有标签分配给它们。我们编写规则来控制进程标签到对象标签(如文件)的访问权限。我们称之为策略。内核强制执行这些规则。有时这种强制执行被称为强制访问控制(MAC)。
对象的拥有者对于其安全属性没有决定权。标准的Linux访问控制,所有者/组+权限标志如rwx,通常称为自由访问控制(DAC)。SELinux没有UID或文件所有权的概念。一切都由标签控制。意味着SELinux并不需要一个全能的root进程来设置。
注:SELinux并不能让你绕过DAC。SELinux是一个并行执行模型。一个应用程序只有在SELinux和DAC都允许的情况下才能进行某些活动。这可能会因为进程权限被拒绝而给系统管理员造成困扰。一般在系统管理员的眼里,权限拒绝意味着DAC配置错误,而不是SELinux标签。
类型强制
让我们更加深入的看一看标签。SELinux的主要模型或者说执行被称为类型强制。基本含义就是,我们根据一个进程的类型为其定义标签,也基于一个文件系统对象的类型为其定义标签。
类比
想象在一个系统里,我们定义像猫和狗这样类型的对象。猫和狗都代表进程类型。
我们有一类对象叫做食物,这些进程想要与之交互。我们想要给这些食物加上类型,猫粮和狗粮。
作为一个策略的编写者,我认为狗有吃狗粮的权限,猫有吃猫粮的权限。在SELinux中,我们像下面这样编写规则。
allow cat cat_chow:food eat;
allow dog dog_chow:food eat;
有了这些规则,内核会允许猫进程吃标有cat_chow标签的食物,允许狗进程吃标有dog_chow标签的食物。
但是SELinux系统默认会阻止一切操作。也就是说,如果狗进程尝试去吃猫粮,内核将会阻止它。
同样,猫也不允许吃狗粮。
译者注:任何操作,如果SELinux规则集中找不到与之匹配的允许规则,就会被阻止。也就是空的规则集就会阻止一切,所以我们需要编写规则来允许特定的操作。
现实世界
我们将Apache服务器进程标上httpd_t
标签,我们把Apache服务器需要访问的内容标上httpd_sys_content_t
标签和httpd_sys_content_rw_t
标签。想象我们把信用卡数据存储在MySQL数据库里,我们为它标上msyqld_data_t
标签。如果一个Apache进程被黑客攻击了,黑客将会得到httpd_t
进程的控制权,会被允许读取httpd_sys_content_t
标签类型的文件和读写httpd_sys_content_rw_t
标签类型的文件。但是黑客不会被允许读取信用卡数据(mysqld_data_t
标签类型)即使进程是以root身份运行的。这种情况下SELinux减轻了入侵造成的危害。
MCS强制
类比
以上,我们把进程按猫和狗进行分类,但是当你有多个狗进程时将会发生什么:两只狗Fido和Spot。你想要阻止Fido去吃Spot的dog_chow。
一种解决方案是创建更多的新类型,像Fido_dog和Fido_dog_chow。但是,这将很快变得难以驾驭,因为所有的狗的权限几乎是相同的。
为了处理这种情况,我们开发了一种新的强制执行形式,称为多类别安全(MCS)。在MCS中,我们在狗进程和狗粮dog_chow的标签中添加了一个新的部分。现在,我们把狗进程标为dog:random1
(Fido)和dog:random2
(Spot)。
我们把狗粮标为dog_chow:random1
(Fido)和dog_chow:random2
(Spot)。
在MCS中,如果类型强制规则验证通过,并且随机的MCS标签也完全匹配,那么访问就会被允许,不然就会被拒绝。
- Fido(
dog:random1
)尝试去吃cat_chow:food
被类型强制拒绝。
- Fido(
dog:random1
)允许吃dog_chow:random1
。
- Fido(
dog:random1
)不允许吃Spot的食物(dog_chow:random2
)。
现实世界
在计算机系统中,我们通常有很多的进程有着相同的访问权限,但是我们想要把它们彼此分开。我们有时称之为多租户环境。最好的例子就是虚拟机。如果我有一台运行大量虚拟机的服务器,而且其中一台遭到黑客攻击,我想要阻止它攻击其他的虚拟机和虚拟机映像。但是在类型强制系统中KVM虚拟机被标为svirt_t
,而虚拟机映像被标为svirt_image_t
。我们的规则允许svirt_t
读、写和删除svirt_image_t
类型的内容。在libvirt中我们不仅实现了类型强制,而且实现了MCS。当libvirt将要启动一个虚拟机时,它会选择一个随机的MCS标签,如s0:c1,c2
,然后将标签svirt_image_t:s0:c1,c2
分配给虚拟机接下来需要管理的所有内容。最终,它以svirt_t:s0:c1,c2
类型启动虚拟机。然后,SELinux内核会控制svirt_t:s0:c1,c2
不能写入svirt_image_t:s0:c3,c4
,即使虚拟机被黑客完全控制了。即使它是以root身份运行的。
MLS强制
另一种SELinux强制执行形式,使用的频率相对较少,称为多级安全(MLS);它的开发是在上世纪60年代,主要应用在受信任的操作系统上,例如Trusted Solaris。
其主要思想是根据将要使用的数据的级别来控制进程。一个secret
级进程不能读取top secret
级数据。
MLS与MCS非常相似,但是它给强制执行增加了优势度的概念。MCS标签必须精确匹配,而一个MLS标签可以比另一个MLS标签更有优势,从而获得访问权限。
类比
现在我们来看看不同的品种,而不再谈论不同的狗。我们可能会有一只灰狗和一只吉娃娃。
我们可能会允许灰狗吃任何的狗粮,但如果吉娃娃吃灰狗的狗粮会有噎住窒息的危险。
我们把灰狗标上dog:Greyhound
标签,把它的狗粮标上dog_chow:Greyhound
,并且为吉娃娃标上dog:Chihuahua
标签,也为它的狗粮标上dog_chow:Chihuahua
。
有了MLS策略,我们使MLS Greyhound标签更优势于Chihuahua标签。这意味着dog:Greyhound
被允许吃dog_chow:Greyhound
和dog_chow:Chihuahua
。
但是不允许dog:Chihuahua
吃dog_chow:Greyhound
。
当然,dog:Greyhound
和dog:Chiahuahua
吃cat_chow:Siamese
依然会被类型强制给阻止,即使MLS Greyhound优于Siamese。
现实世界
我可能会有两个Apache服务器,一个以httpd_t:TopSecret
标签运行,另一个以httpd_t:Secret
标签运行。如果有着httpd_t:Secret
标签的Apache进程被黑客攻击了,黑客能够读取httpd_sys_content_t:Secret
类型的数据,但是读取httpd_sys_content_t:TopSecret
类型会被阻止。
然而,如果运行在httpd_t:TopSecret
的Apache服务器被黑客攻击了,它既能够读取httpd_sys_content_t:Secret
类型的数据,又能够读取httpd_sys_content_t:TopSecret
类型的数据。
我们在军用环境里使用MLS,在那里某个用户只允许读取secret
级数据,但是同一个系统里的另一个用户能够读取top secret
级的数据。
结论
SELinux是一个强大的标签系统,控制着内核授予不同进程的访问权限。主要特性是类型强制,定义的规则根据进程的标签类型和受访问对象的标签类型来控制是否允许访问。还有两种附加的控制机制,MCS用于把同类型的进程完全的彼此分开,MLS允许按优势度控制进程的访问。