Linux内核中包含了多个安全子系统,它们之间相互独立又有着千丝万缕的联系,导致人们对它们往往有一种雾里看花的感觉。在这篇文章里,我们对Linux内核中的安全子系统做一个简单的分析,算是一个入门。
Linux内核的安全子系统大概包括下面几个:
- 访问控制(AC,Access Control),它是最常见也是最重要的安全机制。AC主要是要管理Linux下的一个主体(如进程)到底对一个客体(如文件)是否能采取某些访问操作(如读取文件内容)。访问控制又进一步包括:
- 自主访问控制(DAC),包括访问控制列表(ACL)以及权能模型(POSIX capabilities)
- 强制访问控制(MAC),包括很多人都听说过的SELinux与AppArmor都是MAC
- 用户验证(Authentication),它主要指的是如何识别一个真实的、物理的人是Linux系统中的一个注册账号
- 资源隔离,将进程之间的资源(包括文件、进程、网络等)隔离开,使得应用程序运行互不影响
- 配额限制,对软件进行资源配额限制,例如限定某软件或者某些软件最多占用多少资源等
- 系统沙箱,限定应用程序是否能调用某些系统调用,或者以某种参数调用某些系统调用
- 可信计算,对系统的完整性进行校验,例如从硬件校验bootloader的完整性,从bootloader校验内核的完整性等,一般的完整性校验都是通过预先对原内容进行hash计算得到一个散列值存储在安全区,再在正式使用数据之前校验散列值是否有变化的方式来实现的
- 安全审计,在系统运行过程中记录安全日志,在安全事件发生后,通过安全审计可以追查当时的场景,定位原因,修补漏洞,以避免再次发生类似的安全问题
- 透明加密,对数据进行多层次的透明加密,以保证在没有得到密钥的情况下,被加密保护的数据无法被恢复出明文,而且在得到正确密钥的情况下,应用程序能像访问明文数据一样访问密文数据
- 网络防护,即对通过网络进出本系统的数据进行安全检查
下面,我们稍微展开说一下各个安全子系统。
访问控制
在Linux操作系统下,自主访问控制指的是文件的属主(owner)可以设置文件被系统中所有用户访问的权限。
常见的DAC设置命令是chmod,所有用户被划分为三类,即属主(u,user),与属主同组的用户(g,group)以及其它用户(o,others),访问权限则包括读(r,read)、写(w,write)与执行(x,execute),由于用户与访问权限都是三个,因此一般会使用三个八进制的数字表示权限。
例如将文件file1的权限设置为属主可读可写,同组用户与其他用户可读,则可以运行chmod 0644 file1,其中6即为二进制的100 + 二进制的10,表示对属主可读与可写,4即为二进制的100,分别表示对同组可读,以及对其它用户可读。
上述文件的权限被保存在文件的inode中,进程的安全凭证则是在内核对应的task_struct的struct cred real_cred与struct cred cred中,在打开文件时内核会对inode中的文件权限与进程中的安全凭证进行对比与DAC检查。这种权限设置方法是从Unix沿袭而来的访问控制机制。
由于传统的DAC设置用户分类粒度太粗,因此为了提供细粒度的读写执行的访问控制,Linux又引入了访问控制列表,可以针对某个用户或者用户组设置单独的访问权限,使用的命令是setfacl与getfacl,对应的设置经过编码保存在文件的扩展属性system.posix_acl_access中。
在Linux系统下有一个系统管理员账号,即root。它是一个bug级的存在,普通用户设置的读写权限对于root来说是没有作用的,root可以跳过DAC直接读取、删除、修改普通用户的任何文件。也就是说一旦一个人拥有了root权限,他实际上就可以在Linux下为所欲为了。而有些功能既需要访问系统级的资源,从而需要有root权限,又需要被普通用户使用,因此*nix又发明了setuid,用来将某个属主为root的可执行程序设置为普通用户可用,在普通用户使用此程序时拥有root的权限,退出此程序时则恢复普通用户的权限。此类程序包括需要创建原始套接字的ping、需要修改/etc/shadow文件等的passwd等。
这种设置带来的安全风险很大,一旦此类程序中可能存在的安全漏洞被利用,攻击者即可对整个系统为所欲为。因此,Linux又遵循POSIX的权能模型(capabilities),将传统的root权限分解为了多个子权限,例如审计、修改属主、发信号、网络管理、原始套接字等。这样,某个原本需要setuid权限的程序可以通过setcap等命令为程序设置相应的最小权能,即使相应的程序被攻击者利用获得了特权,攻击者能获得的也只有很小的一部分特权,无法为所欲为。
不过,DAC最大的问题是资源的访问权限控制都是由资源的属主来设置的,而没有安全意识的用户往往不知道应该如何设置访问权限以保证整体安全,毕竟,安全漏洞往往是出在最短板上的。因此,对于DAC,它最大的问题是“不怕神一样的对手,就怕猪一样的队友”。
为了解决猪队友的问题,强制访问控制(MAC)被提上了日程。强制访问控制定义了一整套安全访问控制的体系,系统管理员(或安全管理员)能够通过实施MAC,限定远比DAC更细致更广泛的访问控制。例如在MAC下,我们可以限定即使都是由root启动,但是只有由init启动的nginx才能绑定80端口,但是从bash启动的nginx则只能绑定8080端口。
最著名的MAC是美国国家安全局(NSA)开发的SELinux,它也是使用最为广泛的MAC,在Android中也使用到了它。SELinux提供了安全策略,例如基于类型的TE(Type Enforcement)、基于密级的MLS(MultiLevel Security),以及同密级下不同分类的MCS(MultiCategory Security)等,此外,通过角色与用户的划分,SELinux还提供了基于角色的访问控制(RBAC)。
不过SELinux虽然可以避免猪队友的问题,但是它的细粒度管理方式对管理员来说实在是太麻烦了。缺省情况下它提供了几个用户、十几个角色、上千个分类、数千个类型、上万个类型转换,要正确理解、使用甚至修改这些规则,对于任何管理员来说都是极大的负担,而且新的软件仍然在源源不断地开发出来,旧有的软件在持续升级与复杂化,这使得事后访问控制的软件兼容负担日益加重。
用户验证
Linux在用户验证上提供了一种灵活的机制,那就是PAM(可插拔认证模块)。
PAM提供了一个所有服务的中心验证机制,适用于普通登录、ssh登录等需要进行身份认证的系统中。在Linux操作系统中存在多个PAM模块,例如负责使用用户名密码对用户进行认证的pam_unix.so模块,负责拒绝用户认证的pam_deny.so模块,负责通过用户认证的pam_permit.so模块等。
一般通过文本方式通过本地进入系统使用的程序为login,通过网络远程进入系统使用的程序为sshd,通过图形界面方式通过本地进入系统使用的程序为lightdm。上述的每个负责鉴别用户与登录的程序都将通过PAM机制对用户进行认证。
login等程序并不进行实质的Linux用户认证,具体的认证方式都是通过Linux系统管理员对PAM模块的配置完成的。例如最常见的配置是使用上述的pam_unix.so,要求用户输入用户名与密码进行认证。但是系统管理员也可以配置pam_fprintd要求用户在使用login或者lightdm登录的时候通过指纹进行用户认证,而这些认证方式对使用了PAM的login等程序而言,都是透明的。
使用了PAM的优点在于,一方面,将用户鉴别功能从每个应用软件中独立出来,单独进行模块化设计,实现和维护;另一方面,为这些鉴别模块建立标准 API,以便各应用程序能方便的使用它们提供的各种功能;同时,鉴别机制对其上层用户(包括应用程序和最终用户)是透明的,便于使用与维护。
PAM采用了分层的模块式开发,提供了四种类型的模块:
- 认证管理(auth),负责进行用户认证,这也是PAM模块的主要功能,它可以提供不同的认证方式,对用户进行组合认证
- 账号管理(account),负责管理用户账户的有效性,例如账户是否过期了,账户是否可以允许访问对应的服务等
- 会话管理(session),负责在用户建立会话之前或者销毁会话之后处理工作,例如记录审计日志,或者挂载/卸载用户的家目录等
- 口令管理(password),负责更新用户认证的机制,例如常见的要求输入确认密码的场景,以及要求密码复杂度的场景
PAM的配置文件通常在/etc/pam.d/下。
模块将按照在配置文件中列出的顺序被调用,这取决于每个条目允许的控制标记(control flag)的值。常见的控制标记值包括:
- required:所有 required 模块都必须运行到底,如果中间产生了失败,则失败信息将会被记录下来,到最后返回第一个失败的信息,如果都成功了,则返回成功
- sufficient:如果标记为 sufficient 的模块返回成功,并且先前没有 required 或 sufficient 模块失败,则忽略剩余的其余模块并直接返回认证成功。
- optional:如果PAM模块中没有一个模块是 required 的,并且没有任何一个 sufficient 模块成功,则至少要有一个 optional 模块返回成功才算成功
实际上,上述的控制标记值只是一个简化的标记而已,在PAM配置时可以配置数十个参数,例如success、open_err、new_authtok_reqd、default、ignore等,相应的值包括ok、ignore、bad、done、die等。上述的required实际上就是success=ok new_authtok_reqd=ok ignore=ignore default=bad 的缩写,而sufficient则是 success=done new_authtok_reqd=done default=ignore 的缩写。
下面是login程序使用到的PAM配置文件(文件名为/etc/pam.d/login)的样例。
在上述配置文件中,以#开始的每行文字是注释(已经删除了大部分),以auth开始的每行文字是认证相关的PAM模块及其配置,以session开始的每行文字是会话相关的PAM模块及其配置,而以@include开头的行则是将其它PAM配置文件的内容导入了本配置文件,这样可以提供一定的模块化与较好的可维护性。值得注意的是,这里的PAM配置文件都是自动生成的,里面的注释也比较详细,可以提供较好的手工修改与管理的基础。
当前PAM模块面临的最主要的问题是:
传统PAM体系与各个模块是基于console的,对于图形界面支持仍需改进
大量新型认证方式(如人脸识别、虹膜识别、指纹识别等)需要我们开发相应的PAM模块以提供支持
附录:
(1)https://distrowatch.com/index.php?distribution=deepin
(2)https://www.deepin.org/zh/kernel-security-subsystem-1/
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。