当前位置: 首页 > news >正文

【Docker 内核详解】namespace 资源隔离(五):User namespaces

【Docker 内核详解 - namespace 资源隔离】系列包含:

  • namespace 资源隔离(一):进行 namespace API 操作的 4 种方式
  • namespace 资源隔离(二):UTS namespace & IPC namespace
  • namespace 资源隔离(三):PID namespace
  • namespace 资源隔离(四):Mount namespace & Network namespace
  • namespace 资源隔离(五):User namespaces

namespace 资源隔离(五):User namespaces

user namespace 主要隔离了安全相关的 标识符identifier)和 属性attribute),包括用户 ID、用户组 ID、root 目录、key(指密钥)以及特殊权限。通俗地讲,一个普通用户的进程通过 clone() 创建的新进程在新 user namespace 中可以拥有不同的用户和用户组。这意味着一个进程在容器外属于一个没有特权的普通用户,但是它创建的容器进程却属于拥有所有权限的超级用户,这个技术为容器提供了极大的自由。

user namespace 是目前的 6 6 6namespace 中最后一个支持的,并且直到 Linux 内核 3.8 3.8 3.8 版本的时候还未完全实现(还有部分文件系统不支持)。user namespace 实际上并不算完全成熟,很多发行版担心安全问题,在编译内核的时候并未开启 USER_NS。Docker 在 1.10 1.10 1.10 版本中对 user namespace 进行了支持。只要用户在启动 Docker daemon 的时候指定了 --userns-remap,那么当用户运行容器时,容器内部的 root 用户并不等于宿主机内的 root 用户,而是映射到宿主上的普通用户。在进行接下来的代码实验时,请确保系统的 Linux 内核版本高于 3.8 3.8 3.8 并且内核编译时开启了 USER_NS(如果不会选择,请使用 Ubuntu 14.04 14.04 14.04)。

Linux 中,特权用户的 user ID 就是 0 0 0,演示的最后将看到 user ID 非 0 0 0 的进程启动 user namespace 后 user ID 可以变为 0 0 0。使用 user namespace 的方法跟别的 namespace 相同,即调用 clone()unshare() 时加入 CLONE_NEWUSER 标识位。修改代码并另存为 userns.c,为了看到用户权限(Capabilities),还需要安装 libcap-dev 包。

首先包含以下头文件以调用 Capabilities 包。

#include <sys/capability.h>

其次在子进程函数中加入 geteuid()getegid() 得到 namespace 内部的 user ID,通过 cap_get_proc() 得到当前进程的用户拥有的权限,并通过 cap_to_text() 输出。

int child_main(void* args){printf("在子进程中!\n");cap_t caps;printf("eUID = %ld; eGID = %ld; ", (long) geteuid(), (long) getegid());caps = cap_get_proc();printf("capabilities: %s\n", cap_to_text(caps, NULL));execv(child_args[0], child_args);return 1;
}

在主函数的 clone() 调用中加人我们熟悉的标识符。

// [...]
int child_pid = clone(child_main, child_stack + STACK_SIZE, CLONE_NEWUSER | SIGCHLD, NULL);
// [...]

至此,第一部分的代码修改就结束了。在编译之前先查看一下当前用户的 uidguid,请注意此时显示的是普通用户。

$ id -u
1000
$ id -g
1000

然后开始编译运行,并进入新建的 user namespace,会发现 shell 提示符前的用户名已经变为 nobody

$ gcc userns.c -Wall -lcap -o userns.o && ./userns.o
程序开始:
在子进程中!
eUID=65534; eGID=65534; capabilities: = cap_chown,cap_dac_override,[...]37+ep     <<--此处省略部分输出,已拥有全部权限
nobody@ubuntu$

通过验证可以得到以下信息。

  • user namespace 被创建后,第一个进程被赋予了该 namespace 中的全部权限,这样该 init 进程就可以完成所有必要的初始化工作,而不会因权限不足出现错误。
  • namespace 内部观察到的 UID 和 GID 已经与外部不同了,默认显示为 65534 65534 65534,表示尚未与外部 namespace 用户映射。此时需要对 user namespace 内部的这个初始 user 和它外部 namespace 的某个用户建立映射,这样可以保证当涉及一些对外部 namespace 的操作时,系统可以检验其权限(比如发送一个信号量或操作某个文件)。同样用户组也要建立映射。
  • 还有一点虽然不能从输出中发现,但却值得注意。用户在新 namespace 中有全部权限,但它在创建它的父 namespace 中不含任何权限,就算调用和创建它的进程有全部权限也是如此。因此哪怕是 root 用户调用了 clone()user namespace 中创建出的新用户,在外部也没有任何权限。
  • 最后,user namespace 的创建其实是一个层层嵌套的树状结构。最上层的根节点就是 root namespace,新创建的每个 user namespace 都有一个父节点 user namespace,以及零个或多个子节点 user namespace,这一点与 PID namespace 非常相似。

从下图中可以看到,namespace 实际上就是按层次关联起来,每个 namespace 都发源于最初的 root namespace 并与之建立映射。

在这里插入图片描述
接下来就要进行用户绑定操作,通过在 /proc/[pid]/uid_map/proc/[pid]/gid_map 两个文件中写入对应的绑定信息就可以实现这一点,格式如下。

ID-inside-ns ID-outside-ns length

写这两个文件时需要注意以下几点。

  • 这两个文件只允许由拥有该 user namespaceCAP_SETUID 权限的进程写入一次,不允许修改。
  • 写入的进程必须是该 user namespace 的父 namespace 或者子 namespace
  • 第一个字段 ID-inside-ns 表示新建的 user namespace 中对应的 user/group ID,第二个字段 ID-outside-ns 表示 namespace 外部映射的 user/group ID。最后一个字段表示映射范围,通常填 1 1 1,表示只映射一个,如果填大于 1 1 1 的值,则按顺序建立一一映射。

明白了上述原理,再次修改代码,添加设置 uidgid 的函数。

// [...]
void set_uid_map(pid_t pid, int inside_id, int outside_id, int length) {char path[256];sprintf(path, "/proc/%d/uid_map", getpid());FILE* uid_map = fopen(path, "w");fprintf(uid_map, "%d %d %d", inside_id, outside_id, length);fclose(uid_map);
}
void set_gid_map(pid_t pid, int inside_id, int outside_id, int length) {char path[256];sprintf(path, "/proc/%d/gid_map", getpid());FILE* gid_map = fopen(path, "w");fprintf(gid_map, "%d %d %d", inside_id, outside_id, length);fclose(gid_map);
}
int child_main(void* args){cap_t caps;printf("在子进程中!\n")set_uid_map(getpid(), 0, 1000, 1);set_gid_map(getpid(), 0, 1000, 1);printf("eUID = %ld; eGID = %ld; ", (long) geteuid(), (long) getegid());caps = cap_get_proc();printf("capabilities: %s\n", cap_to_text(caps, NULL));execv(child_args[0], child_args);return 1;
}
// [...]

编译后即可看到 user 已经变成了 root

$ gcc uscrns.c -Wall -lcap -o usernc.o && ./usernc.o
程序开始:
在子进程中!
eUID = 0; eGID = 0; capabilities: = [..],37+ep
root@ubuntu:~#

至此,就已经完成了绑定的工作,可以看到演示全程都是在普通用户下执行的,最终实现了在 user namespace 中成为 root 用户,对应到外部则是一个 uid 1000 1000 1000 的普通用户。

如果要把 user namespace 与其他 namespace 混合使用,那么依旧需要 root 权限。解决方案是先以普通用户身份创建 user namespace,然后在新建的 namespace 中作为 root,在 clone() 进程加入其他类型的 namespace 隔离。

讲解完 user namespace,再来谈谈 Docker。Docker 不仅使用了 user namespace,还使用了在 user namespace 中涉及的 Capabilities 机制。从内核 2.2 2.2 2.2 版本开始,Linux 把原来和超级用户相关的高级权限划分为不同的单元,称为 Capability。这样管理员就可以独立对特定的 Capability 进行使用或禁止。Docker 同时使用 user namespace 和 Capability,这在很大程度上加强了容器的安全性。

说到安全,namespace 6 6 6 项隔离看似全面,实际上依旧没有完全隔离 Linux 的资源,比如 SELinuxcgroups/sys/proc/sys/dev/sd* 等目录下的资源。关于安全,将会在后续博客中进一步探讨。


本系列从 namespace 使用的 API 开始,结合 Docker 逐步对 6 个 namespace 进行了讲解。相信把讲解过程中所有的代码整合起来,读者也能实现一个属于自己的 “shell” 容器了。虽然 namespace 技术使用非常简单,但要真正把容器做到安全易用却并非易事。PID namespace 中,需要实现一个完善的 init 进程来维护好所有进程;network namespace 中,还有复杂的路由表和 iptables 规则没有配置;user namespace 中还有许多权限问题需要考虑。其中的某些方面 Docker 已经做得不错,而有些方面才刚刚起步,这些内容我们会在后续博客中详细介绍。

相关文章:

【Docker 内核详解】namespace 资源隔离(五):User namespaces

【Docker 内核详解 - namespace 资源隔离】系列包含&#xff1a; namespace 资源隔离&#xff08;一&#xff09;&#xff1a;进行 namespace API 操作的 4 种方式namespace 资源隔离&#xff08;二&#xff09;&#xff1a;UTS namespace & IPC namespacenamespace 资源隔…...

网络原理必知会

衔接上文&#xff1a;网络原理必知会_念君思宁的博客-CSDN博客 流量控制&#xff1a; 流量控制也是保证可靠性的机制 对于滑动窗口&#xff0c;批量发送数据而言&#xff0c;窗口越大&#xff0c;相当于批量发送的数据越多&#xff0c;整体的速度也就越快了&#xff0c;但是&…...

ELK 日志分析系统介绍与部署

目录 一、ELK 简介: 1.开源工具介绍&#xff1a; 2.其它组件&#xff1a; 2.1 Filebeat&#xff1a; 2.2 Fluentd&#xff1a; 2.3 缓存/消息队列&#xff08;redis、kafka、RabbitMQ等&#xff09;&#xff1a; 3. filebeat 结合 logstash 带来好处&#xff1a; 二、为什么要…...

Android 内存治理之线程

1、 前言 当我们在应用程序中启动一个线程的时候&#xff0c;也是有可能发生OOM错误的。当我们看到以下log的时候&#xff0c;就说明系统分配线程栈失败了。 java.lang.OutOfMemoryError: pthread_create (1040KB stack) failed: Out of memory这种情况可能是两种原因导致的。…...

三、K8S之ReplicaSet

ReplicaSet 一、概述 Kubernetes最核心的功能是编排&#xff0c;编排操作都是依靠控制器对象来完成&#xff0c;高级控制器控制着基础的控制器&#xff0c;基础控制器再去控制Pod&#xff0c;Pod里面再包容器。K8S项目里API对象层级大概就是这样。 而ReplicaSet这个控制器是…...

【基础篇】四、本地部署Flink

文章目录 1、本地独立部署会话模式的Flink2、本地独立部署会话模式的Flink集群3、向Flink集群提交作业4、Standalone方式部署单作业模式5、Standalone方式部署应用模式的Flink Flink的常见三种部署方式&#xff1a; 独立部署&#xff08;Standalone部署&#xff09;基于K8S部署…...

简述什么是迭代器(Iterator)?

迭代器(Iterator)是一种设计模式,Java 中的迭代器是集合框架中的一个接口,它可以让程序员遍历集合中的元素而无需暴露集合的内部结构。使用迭代器可以遍历任何类型的集合,例如 List、Set 和 Map 等。 通过调用集合类的 iterator() 方法可以获取一个迭代器,并使用 hasNext…...

DarkGate恶意软件通过消息服务传播

导语 近日&#xff0c;一种名为DarkGate的恶意软件通过消息服务平台如Skype和Microsoft Teams进行传播。它冒充PDF文件&#xff0c;利用用户的好奇心诱使其打开&#xff0c;进而下载并执行恶意代码。这种攻击手段使用了Visual Basic for Applications&#xff08;VBA&#xff0…...

LeetCode——动态规划篇(六)

刷题顺序及思路来源于代码随想录&#xff0c;网站地址&#xff1a;https://programmercarl.com 目录 300. 最长递增子序列 - 力扣&#xff08;LeetCode&#xff09; 674. 最长连续递增序列 - 力扣&#xff08;LeetCode&#xff09; 718. 最长重复子数组 - 力扣&#xff08…...

sql 注入(2), 文件读写 木马植入 远程控制

sql 注入 文件读写 木马植入 远程控制 一, 检测读写权限 查看mysql全局变量 SHOW GLOBAL VARIABLES LIKE %secure%secure_file_priv 空, 则任意读写secure_file_priv 路径, 则只能读写该路径下的文件secure_file_priv NULL, 则禁止读写二, 读取文件, 使用 load_file() 函数…...

求直角三角形第三点的坐标

文章目录 求直角三角形第三点的坐标1. 原理2. 数学公式3. 推导过程 求直角三角形第三点的坐标 1. 原理 已知内容有&#xff1a; P1、P2 两点的坐标&#xff1b; dis1 为 P1与P2两点之间的距离&#xff1b; dis2 为 P2与P3两点之间的距离&#xff1b; 求解&#xff1a; …...

【Kotlin精简】第3章 类与接口

1 简介 Kotlin类的声明和Java没有什么区别&#xff0c;Kotlin中&#xff0c;类的声明也使用class关键字&#xff0c;如果只是声明一个空类&#xff0c;Kotlin和Java没有任何区别&#xff0c;不过定义类的其他成员会有一些区别。实例化类不用写new&#xff0c;类被继承或者重写…...

关于面试以及小白入职后的一些建议

面试的本质 面试的过程是一个互相选择的过程&#xff1b;面试官的诉求是&#xff0c;了解应聘者的个人基本信息、工作态度、专业能力及其他综合能力是否与公司招聘岗位匹配&#xff1b;面试者的诉求是&#xff0c;拿下招聘岗位offer&#xff0c;获得工作报酬&#xff1b; 面试…...

Excel 从网站获取表格

文章目录 导入网站数据导入股票实时行情 用 Excel 获取网站数据的缺点&#xff1a;只能获取表格类的数据&#xff0c;不能获取非结构化的数据。 导入网站数据 转到地址之后&#xff1a; 实测该功能经常导致 Excel 卡死。 导入股票实时行情...

rsync 备份工具(附rsync+inotify 实时同步部署实例)

rsync 备份工具(附rsyncinotify 实时同步部署实例&#xff09; 1、rsync概述1.1关于rsync1.2rsync 的特点1.3工作原理 2、rsync相关命令2.1基本格式和常用选项2.2启动和关闭rsync服务2.3下行同步基本格式2.4上行同步基本格式2.5免交互2.5.1指定密码文件2.5.2rsync-daemon方式2.…...

Java架构师缓存性能优化

目录 1 缓存的负载策略2 缓存的序列化问题3 缓存命中率低4 缓存对数据库高并发访问5 缓存数据刷新的策略5.1. 实时策略5.2. 异步策略5.3. 定时策略6 何时写缓存7 批量数据来更新缓存8 缓存数据过期的策略9 缓存数据如何恢复10 缓存数据如何迁移11 缓存冷启动和缓存预热想学习架…...

探索服务器潜能:创意项目、在线社区与其他应用

目录 一、部署自己的创意项目 优势&#xff1a; 劣势&#xff1a; 结论&#xff1a; 二、打造一款全新的在线社区 优势&#xff1a; 劣势&#xff1a; 结论&#xff1a; 三、其他用途 总结&#xff1a; 随着互联网的发展&#xff0c;越来越多的人开始拥有自己的服务器…...

「网络编程」网络层协议_ IP协议学习_及深入理解

「前言」文章内容是网络层的IP协议讲解。 「归属专栏」网络编程 「主页链接」个人主页 「笔者」枫叶先生(fy) 目录 一、IP协议简介二、IP协议报头三、IP网段划分&#xff08;子网划分&#xff09;四、特殊的IP地址五、IP地址的数量限制六、私有IP地址和公网IP地址七、路由八、分…...

Go 1.21 新内置函数:min、max 和 clear

max 函数 func max[T cmp.Ordered](x T, y …T) T 这是一个泛型函数&#xff0c;用于从一组值中寻找并返回 最大值&#xff0c;该函数至少要传递一个参数。在上述函数签名中&#xff0c;T 表示类型参数&#xff0c;它必须满足 cmp.Ordered 接口中定义的数据类型要求&#xff0…...

家居行业如何打破获客困局?2023重庆建博会现场,智哪儿AI营销第一课给出了答案

10月12日-14日&#xff0c;2023中国&#xff08;重庆&#xff09;建筑及装饰材料博览会&#xff08;简称&#xff1a;2023中国重庆建博会&#xff09;正在重庆国际博览中心如火如荼地进行。「智哪儿」携手2023中国重庆建博会主办方共同主办的《2023家居行业AI营销第一课&#x…...

Linux相关概念和易错知识点(42)(TCP的连接管理、可靠性、面临复杂网络的处理)

目录 1.TCP的连接管理机制&#xff08;1&#xff09;三次握手①握手过程②对握手过程的理解 &#xff08;2&#xff09;四次挥手&#xff08;3&#xff09;握手和挥手的触发&#xff08;4&#xff09;状态切换①挥手过程中状态的切换②握手过程中状态的切换 2.TCP的可靠性&…...

JVM垃圾回收机制全解析

Java虚拟机&#xff08;JVM&#xff09;中的垃圾收集器&#xff08;Garbage Collector&#xff0c;简称GC&#xff09;是用于自动管理内存的机制。它负责识别和清除不再被程序使用的对象&#xff0c;从而释放内存空间&#xff0c;避免内存泄漏和内存溢出等问题。垃圾收集器在Ja…...

蓝桥杯3498 01串的熵

问题描述 对于一个长度为 23333333的 01 串, 如果其信息熵为 11625907.5798&#xff0c; 且 0 出现次数比 1 少, 那么这个 01 串中 0 出现了多少次? #include<iostream> #include<cmath> using namespace std;int n 23333333;int main() {//枚举 0 出现的次数//因…...

SQL慢可能是触发了ring buffer

简介 最近在进行 postgresql 性能排查的时候,发现 PG 在某一个时间并行执行的 SQL 变得特别慢。最后通过监控监观察到并行发起得时间 buffers_alloc 就急速上升,且低水位伴随在整个慢 SQL,一直是 buferIO 的等待事件,此时也没有其他会话的争抢。SQL 虽然不是高效 SQL ,但…...

HybridVLA——让单一LLM同时具备扩散和自回归动作预测能力:训练时既扩散也回归,但推理时则扩散

前言 如上一篇文章《dexcap升级版之DexWild》中的前言部分所说&#xff0c;在叠衣服的过程中&#xff0c;我会带着团队对比各种模型、方法、策略&#xff0c;毕竟针对各个场景始终寻找更优的解决方案&#xff0c;是我个人和我司「七月在线」的职责之一 且个人认为&#xff0c…...

《Docker》架构

文章目录 架构模式单机架构应用数据分离架构应用服务器集群架构读写分离/主从分离架构冷热分离架构垂直分库架构微服务架构容器编排架构什么是容器&#xff0c;docker&#xff0c;镜像&#xff0c;k8s 架构模式 单机架构 单机架构其实就是应用服务器和单机服务器都部署在同一…...

如何在Windows本机安装Python并确保与Python.NET兼容

✅作者简介&#xff1a;2022年博客新星 第八。热爱国学的Java后端开发者&#xff0c;修心和技术同步精进。 &#x1f34e;个人主页&#xff1a;Java Fans的博客 &#x1f34a;个人信条&#xff1a;不迁怒&#xff0c;不贰过。小知识&#xff0c;大智慧。 &#x1f49e;当前专栏…...

针对药品仓库的效期管理问题,如何利用WMS系统“破局”

案例&#xff1a; 某医药分销企业&#xff0c;主要经营各类药品的批发与零售。由于药品的特殊性&#xff0c;效期管理至关重要&#xff0c;但该企业一直面临效期问题的困扰。在未使用WMS系统之前&#xff0c;其药品入库、存储、出库等环节的效期管理主要依赖人工记录与检查。库…...

2025年- H71-Lc179--39.组合总和(回溯,组合)--Java版

1.题目描述 2.思路 当前的元素可以重复使用。 &#xff08;1&#xff09;确定回溯算法函数的参数和返回值&#xff08;一般是void类型&#xff09; &#xff08;2&#xff09;因为是用递归实现的&#xff0c;所以我们要确定终止条件 &#xff08;3&#xff09;单层搜索逻辑 二…...

ZYNQ学习记录FPGA(二)Verilog语言

一、Verilog简介 1.1 HDL&#xff08;Hardware Description language&#xff09; 在解释HDL之前&#xff0c;先来了解一下数字系统设计的流程&#xff1a;逻辑设计 -> 电路实现 -> 系统验证。 逻辑设计又称前端&#xff0c;在这个过程中就需要用到HDL&#xff0c;正文…...