user_namespace分析(2)

本文深入分析了Linux内核中的user_namespace实现,讲解了其如何通过proc文件系统进行用户虚拟化,以及在创建、初始化user_namespace时的权能分配。此外,文章还介绍了在LXC中支持user_namespace的具体代码修改,包括用户ID映射的配置解析,并列举了在启动系统级容器时遇到的问题及解决方案,强调了在不同场景下权限检查的重要性。
摘要由CSDN通过智能技术生成

4      User_ns实现与验证

以上分析的三点都是用户安全管理的基础,user_ns只是结合权能实现了一种局部用户的映射。User_ns的作用简单的说就是一个非特权用户可以创建一个user_ns,然后该用户可以作为该命名空间中的root特权用户,其它的跟在宿主机没有两样。在user_ns中该用户可以创建pid、net_ns等命名空间,并对其有root特权控制属于该命名空间的资源。

4.1      User_ns实现分析

1、 user_ns的虚拟化

设计是基于container中的uid与host中的uid的一一映射,这样的映射关系内核是通过proc文件系统实现的,在proc文件系统添加了两个控制参数,/proc/pid/uid和/proc/pid/gid,系统管理员可以操作这两个proc文件来管理user_ns的全局分配,完成用户的虚拟化。举例如下:

uid = 1000  0  100

gid = 1000  0  100

表示新的user_ns中的uid=0,uid=1的用户分别对应host的uid=1000,uid=1001的用户,其它的类似得出。

static const struct file_operationsproc_uid_map_operations = {

        .open                = proc_uid_map_open,

        .write                = proc_uid_map_write,

        .read         = seq_read,

        .llseek              = seq_lseek,

        .release    = proc_id_map_release,

};

以上为proc的注册函数,其中具体代码只是对一些全局变量的解析与复制,详细过程可参见具体代码实现。

2、 user_ns创建初始化

当应用程序新创建一个user_ns,内核会给这个进程的cred安全策略赋予所有的权能,以使得执行创建命令的用户可以成为新user_ns的root用户。未加命名空间之前的代码实现只是将cred完全copy过来。新的代码如下:

intcreate_user_ns(struct cred *new)

{

      struct user_namespace *ns, *parent_ns =new->user_ns;

      kuid_t owner = new->euid;

      kgid_t group = new->egid;

      if (!kuid_has_mapping(parent_ns, owner) ||

         !kgid_has_mapping(parent_ns, group))

               return -EPERM;

 

      ns = kmem_cache_zalloc(user_ns_cachep,GFP_KERNEL);

      if (!ns)

               return -ENOMEM;

      kref_init(&ns->kref);

      ns->parent = parent_ns;

      ns->owner = owner;

      ns->group = group;

      /* 将所有的权能赋值给新的进程 */

      new->securebits = SECUREBITS_DEFAULT;

      new->cap_inheritable = CAP_EMPTY_SET;

      new->cap_permitted = CAP_FULL_SET;

      new->cap_effective = CAP_FULL_SET;

      new->cap_bset = CAP_FULL_SET;

#ifdefCONFIG_KEYS

      key_put(new->request_key_auth);

      new->request_key_auth = NULL;

#endif

      /* tgcred will be cleared in our caller bcCLONE_THREAD won't be set */

      /* Leave the new->user_ns referencewith the new user namespace. */

      /* Leave the reference to our user_ns withthe new cred. */

      new->user_ns = ns;

      return 0;

}

该新进程会与execve的执行进行结合组成新命令执行的上下文环境,具体可参见3.3应用程序运行时设置信任值一节。

3、 资源操作时权能检查的关键函数举例

由于现在系统有了user_ns,对用户进行了虚拟化,现在该user_ns中的用户只能访问属于该user_ns中的资源,所以内核在各个访问资源的入口处应该都有相应的函数判断,这样的函数有好几个,但是功能都大同小异,关键看其应用场景,现举例如下:

boolinode_capable(const struct inode *inode, int cap)

{

      struct user_namespace *ns =current_user_ns();

      return ns_capable(ns, cap) &&kuid_has_mapping(ns, inode->i_uid);

}

该函数的作用检查该user_ns是否具有cap权能,并且通过kuid_has_mapping函数来检查要访问的资源的uid是不是属于该user_ns所对应的uid区域,如果检查通过就返回0,否则返回非0。

4.2      举个实际访问资源例子

1、 访问ext4文件

当在命令行键入如下命令ls –l /home时,shell进程会fork一个新的进程来执行这个命令,当fork新进程时如果设置了user_namespace标志位,则新的进程的安全属性将包括所有的权能位(详见4.1.2 user_ns创建初始化)。接下去该进程会调用execve函数组织新的进程的上下文执行环境,并重新对进程的权能进行计算(详见3.3应用程序运行时权能的设置),如果是user_namespace的root用户则拥有所有的权能。接下去就是文件访问了,首先会进行访问权限的判断对于该命令是读取目录中的文件列表,其访问检查会通过调用generic_permission来检查(详见3.4.3文件系统检查ACL权限),通过检查则返回0,否则不允许访问该资源。

2、 proc文件系统访问

proc文件系统的访问前面过程跟上例一样,只是在调用permission回调函数时有所不同,proc文件系统的注册函数为proc_sys_permission,具体调用了sysctl_perm函数。代码如下:

staticint sysctl_perm(struct ctl_table_root *root, struct ctl_table *table, int op)

{

        int mode;

        if (root->permissions)

                 mode =root->permissions(root, current->nsproxy, table);

        else

                 mode = table->mode;

        return test_perm(mode, op);

}

对于已经局部化的参数来说,其注册了root->permission函数,其中会检查权能,如果进程有此权能则将mod

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值