低代码到底是不是行业毒瘤?一线大厂怎么做的?戳此了解>>> 了解详情
写点什么

还没对 Docker 加以限制?埋下的安全隐患了解一下

2019 年 7 月 31 日

还没对Docker加以限制?埋下的安全隐患了解一下

本文由 dbaplus 社群授权转载


众所周知,Docker 使用 Namespace 进行环境隔离、使用 CGroup 进行资源限制。但是在实际应用中,还是有很多企业或者组织没有使用 Namespace 或者 CGroup 对容器加以限制,从而埋下安全隐患。


本文将简单介绍 Namespace 和 CGroup 的基本原理,再通过具体配置和应用向读者展示如何应用这些技术保护 Docker 容器安全,不过 Namespace 和 CGroup 并不是万能的,他们只是保障 Docker 容器安全的多种方案中的一类而已。


一、Namespace

1、概述

我们可以给容器分配有限的资源,这有助于限制系统和恶意攻击者可用的系统资源。每个容器所能获取的组件有:


  • 网络堆栈;

  • 进程空间;

  • 文件系统实例。


可通过使用 Namespace 来实现限制资源。Namespace 就像一个“视图”,它只显示系统上所有资源的一个子集。这提供了一种隔离形式:在容器中运行的进程不能看到或影响其他容器中的进程或者宿主本身。


以下是一些常见的 Namespace 类型实例。


Namespace 例子:


Cgroup      CLONE_NEWCGROUP   限制root目录IPC         CLONE_NEWIPC      System V IPC, POSIX消息队列Network     CLONE_NEWNET      网络设备、栈、端口等Mount       CLONE_NEWNS       挂载点PID         CLONE_NEWPID      进程IDUser        CLONE_NEWUSER     用户和组IDUTS         CLONE_NEWUTS      主机名和NIS域名
复制代码


Docker run 命令有几个参数和 Namespace 相关:


IPC:      --ipc string IPC namespace to usePID:      --pid string PID namespace to useUser:      --userns string User namespace to useUTS:      --uts string UTS namespace to use
复制代码


2、确定当前 Docker 用户

默认情况下,Docker 守护程序在主机上以 root 用户身份运行。通过列出所有进程,你可以识别 Docker 守护程序运行的用户。


ps aux | grep docker
复制代码


由于守护程序以 root 身份运行,因此启动的任何容器将具有与主机的 root 用户相同的安全上下文。


docker run --rm alpine id
复制代码


这样是有安全风险的:如果 root 用户拥有的文件可从容器访问,则可以由正在运行的容器修改。


3、删除文件

下面让我们看看用 root 用户运行容器的具体风险。


首先,在我们的主机上创建 touch 命令的副本。


sudo cp /bin/touch /bin/touch.bak && ls -lha /bin/touch.bak
复制代码


由于容器的/hos 目录和宿主的/bin 是同一个,因此可以从容器删除宿主上的文件,不信你试试。


docker run -it -v /bin/:/host/ alpine rm -f /host/touch.bak
复制代码


结果,该命令被删的一干二净。


ls -lha /bin/touch.bak
复制代码


在这种情况下,容器能够从主机删除触摸二进制文件。


4、更改容器用户

可以通过更改用户、组上下文以及使用非特权用户运行的容器来规避以上风险。


docker run --user = 1000:1000 --rm alpine id
复制代码


作为无特权用户,将无法删除二进制文件。


$ docker run -it -v /bin/:/host/ alpine rm -f /host/touch.bak$ docker run --user=1000:1000 --rm alpine iduid=1000 gid=1000$ sudo cp /bin/touch /bin/touch.bak$ docker run --user=1000:1000 -it -v /bin:/host/ alpine rm -f /host/touch.bakrm: can't remove '/host/touch.bak': Permission denied
复制代码


但是,如果我们在容器内部需要访问根目录,那么我们仍然会将自己暴露给前一个场景。这是 Namespace 出现的原因。


5、启用用户 Namespace

Docker 建议不要在启用 Namespace 模式和禁用 Namespace 模式之间来回切换 Docker daemon,执行此操作可能会导致镜像权限出现问题。


Namespace 是 Linux 内核安全功能,该功能允许 Namespace 或容器内的 root 用户访问主机上的非特权用户 ID。


6、任务

使用参数 userns-remap 启动 Docker daemon 时,将启用 Namespace。运行以下命令以修改 Docker daemon 设置并重新启动该进程。


curl https://gist.githubusercontent.com/BenHall/bb878c99d06a63cd8ed4d1c0a6941df4/raw/76136ffbca341846619086cfe40ab8e013683f47/daemon.json -o /etc/docker/daemon.json&& sudo service docker restart
复制代码


使用 cat /etc/docker/daemon.json 查看设置。


cat /etc/docker/daemon.json{    "bip":"172.18.0.1/24",    "debug": true,    "storage-driver": "overlay",    "userns-remap": "1000:1000",    "insecure-registries": ["registry.test.training.katacoda.com:4567"]}
复制代码


重新启动后,你可以使用以下命令验证 Namespace 是否到位。


docker info | grep "Root Dir"WARNING: No swap limit supportDocker Root Dir: /var/lib/docker/100000.100000
复制代码


Docker 将不再以 root 用户身份存储数据。相反,所有内容都作为映射用户进行处理。Docker Root Dir 定义了 Docker 为映射用户存储数据的位置。


注意:在现有系统上启用此功能时,需要重新下载 Docker Images。


7、Namespace 保护

启用 Namespace 后,Docker dameon 将以其他用户身份运行。


ps aux | grep dockerd
复制代码


启动容器时,容器内的用户将具有 root 权限。


docker run --rm alpine id
复制代码


但是,用户将无法修改主机上运行的任何内容。


sudo cp / bin / touch /bin/touch.bakdocker run -it -v / bin /:/ host / alpine rm -f /host/touch.bak
复制代码


与此前不同,我们的 ps 命令仍然存在。


ls -lha /bin/touch.bak
复制代码


通过使用 Namespace,可以将 Docker root 用户分开,并提供比以前更强的安全性和隔离性。


uid=0(root)  gid=0(root) groups=0(root),1(bin),2(daemon),3(sys),4(adm),6(disk),10(wheel),11(floppy),20(dialout),26(tape),27(video)$ sudo cp /bin/touch /bin/touch.bak$ docker run -it -v /bin/:/host/ alpine rm -f /host/touch.bakrm: can't remove '/host/touch.bak': Permission denied$ ls -lha /bin/touch.bak-rwxr-xr-x 1 root root 63K Aug 27 03:59 /bin/touch.bak
复制代码


8、使用网络 Namespace

虽然 CGroup 可以限制进程使用的资源,但还需要 Namespace 控制进程的访问权限。


1)例子


启动容器时,将定义并创建网络接口。这为容器提供了唯一的 IP 地址和接口。


[root@host01 ~]# docker run -it alpine ip addr show1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN qlen 1000    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00    inet 127.0.0.1/8 scope host lo       valid_lft forever preferred_lft forever14: eth0@if15: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue state UP    link/ether 02:42:ac:12:00:03 brd ff:ff:ff:ff:ff:ff    inet 172.18.0.3/24 brd 172.18.0.255 scope global eth0       valid_lft forever preferred_lft forever
复制代码


通过将命名空间更改为主机,而不是容器的网络与其接口隔离,该进程将可以访问主机网络接口。


[root@host01 ~]# docker run -it --net=host alpine ip addr show1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN qlen 1000    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00    inet 127.0.0.1/8 scope host lo       valid_lft forever preferred_lft forever    inet6 ::1/128 scope host       valid_lft forever preferred_lft forever2: enp0s3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP qlen 1000    link/ether 02:42:ac:11:00:11 brd ff:ff:ff:ff:ff:ff    inet 172.17.0.17/16 brd 172.17.255.255 scope global enp0s3       valid_lft forever preferred_lft forever    inet6 fe80::b3ad:ecc4:2399:7a54/64 scope link       valid_lft forever preferred_lft forever3: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP    link/ether 02:42:cd:78:f0:22 brd ff:ff:ff:ff:ff:ff    inet 172.18.0.1/24 brd 172.18.0.255 scope global docker0       valid_lft forever preferred_lft forever    inet6 fe80::e9ad:a1a7:8b68:a0d1/64 scope link       valid_lft forever preferred_lft forever5: veth158bc01@if4: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue master docker0 stateUP    link/ether 9e:bc:3d:01:53:95 brd ff:ff:ff:ff:ff:ff    inet6 fe80::ca3e:49ea:e1d0:8755/64 scope link       valid_lft forever preferred_lft forever
复制代码


如果进程监听端口,它们将在宿主接口上被监听并映射到容器。


9、使用 Pid 命名空间

与网络一样,容器可以看到的进程也取决于它所属的命名空间。更改 Pid 命名空间,将允许容器与超出其正常范围的进程进行交互。


1)例子


第一个容器将在其进程名称空间中运行。因此,它可以访问的唯一进程是在容器中启动的进程。


[root@host01 ~]# docker run -it alpine ps auxPID   USER     TIME   COMMAND    1 root       0:00 ps aux
复制代码


将命名空间更改为主机,容器还可以查看系统上运行的所有其他进程。


[root@host01 ~]# docker run -it --pid=host alpine ps auxPID   USER     TIME   COMMAND    1 root       0:00 /usr/lib/systemd/systemd    2 root       0:00 [kthreadd]    4 root       0:00 [kworker/0:0H]    6 root       0:00 [mm_percpu_wq]    7 root       0:00 [ksoftirqd/0]    8 root       0:00 [rcu_sched]    9 root       0:00 [rcu_bh]
复制代码


10、共享命名空间

有时需要提供容器访问主机命名空间,如调试工具,但这被认为是不安全的做法。这是因为你正在打破可能引入漏洞的容器安全模型。


相反,如果需要,请使用共享命名空间来仅访问容器所需的命名空间。


1)例子


第一个容器启动 Nginx 服务器。这将定义一个新的网络和进程命名空间。Nginx 服务器将自身绑定到新定义的网络接口的端口 80。


docker run -d --name http nginx:alpine
复制代码


其他容器现在可以使用语法容器重用此命名空间:<name>。curl 命令下面可以访问在 localhost 上运行的 HTTP 服务器,因为它们共享相同的网络接口。


docker run --net = container:http benhall / curl curl -s localhost
<!DOCTYPE html><html><head><title>Welcome to nginx!</title><style> body { width: 35em; margin: 0 auto; font-family: Tahoma, Verdana, Arial, sans-serif; }</style></head><body><h1>Welcome to nginx!</h1><p>If you see this page, the nginx web server is successfully installed andworking. Further configuration is required.</p>
<p>For online documentation and support please refer to<a href="http://nginx.org/">nginx.org</a>.<br/>Commercial support is available at<a href="http://nginx.com/">nginx.com</a>.</p>
<p><em>Thank you for using nginx.</em></p></body>
复制代码


它还可以查看共享容器中的进程并与之交互。


docker run --pid=container:http alpine ps auxPID   USER     TIME   COMMAND    1 root       0:00 nginx: master process nginx -g daemon off;    6 100        0:00 nginx: worker process    7 root       0:00 ps aux
复制代码


这对于调试工具很有用,例如 strace。这允许你在不更改或重新启动应用程序的情况下为特定容器提供更多权限。


二、CGroup

1、概述

CGroup 可为系统中所运行的任务或进程的用户群组分配资源,比如 CPU 事件、系统内存、网络带宽或者这些资源的组合。一般可以分为下面几种类型:


  • Resource limitation: 限制资源使用,比如内存使用上限以及文件系统的缓存限制。

  • Prioritization: 优先级控制,比如:CPU 利用和磁盘 IO 吞吐。

  • Accounting: 一些审计或一些统计,主要目的是为了计费。

  • Control: 挂起进程,恢复执行进程。


以下是一些常见的 cgroup 类型示例。


CGroups 例子:


--cpu-shares #限制cpu共享--cpuset-cpus #指定cpu占用--memory-reservation #指定保留内存--kernel-memory #内核占用内存--blkio-weight (block IO) #blkio权重--device-read-iops #设备读iops--device-write-iops #设备写iops
复制代码


docker run 中与 CGroup 相关的参数如下:


block IO:      --blkio-weight value          Block IO (relative weight), between 10 and 1000      --blkio-weight-device value   Block IO weight (relative device weight) (default [])      --cgroup-parent string        Optional parent cgroup for the containerCPU:      --cpu-percent int             CPU percent (Windows only)      --cpu-period int              Limit CPU CFS (Completely Fair Scheduler) period      --cpu-quota int               Limit CPU CFS (Completely Fair Scheduler) quota  -c, --cpu-shares int              CPU shares (relative weight)      --cpuset-cpus string          CPUs in which to allow execution (0-3, 0,1)      --cpuset-mems string          MEMs in which to allow execution (0-3, 0,1)Device:      --device value                Add a host device to the container (default [])      --device-read-bps value       Limit read rate (bytes per second) from a device (default [])      --device-read-iops value      Limit read rate (IO per second) from a device (default [])      --device-write-bps value      Limit write rate (bytes per second) to a device (default [])      --device-write-iops value     Limit write rate (IO per second) to a device (default [])Memory:      --kernel-memory string        Kernel memory limit  -m, --memory string               Memory limit      --memory-reservation string   Memory soft limit      --memory-swap string          Swap limit equal to memory plus swap: '-1' to enable unlimited swap      --memory-swappiness int       Tune container memory swappiness (0 to 100) (default -1)
复制代码


2、定义内存限制

可以通过定义上限边界来帮助限制应用程序的内存泄漏或其他程序 bug。


1)例子


docker run -d --name mb100 --memory 100m alpine topda4db4fd6b70501783c172b7459227c6c8e0426784acf1da26760d80eb2403b0
复制代码


容器的内存使用可通过 docker stats 命令查看。


docker stats --no-streamCONTAINER ID        NAME                CPU %               MEM USAGE / LIMIT   MEM %               NET I/O             BLOCK I/OPIDSda4db4fd6b70        mb100               0.00%           440KiB /100MiB         0.43%               6.21kB / 90B        1.06MB / 0B1
复制代码


3、定义 CPU 份额

虽然内存限制定义了设置的最大值,但 CPU 限制基于共享。这些份额是一个进程应该与另一个进程在处理时间上分配的权重。


如果 CPU 处于空闲状态,则该进程将使用所有可用资源。如果第二个进程需要 CPU,则将根据权重共享可用的 CPU 时间。


1)例子


下面是启动具有不同共享权重的容器的示例。


–cpu-shares 参数定义 0-768 之间的共享。如果容器定义了 768 的份额,而另一个容器定义了 256 的份额,则第一个容器将具有 50%的份额,而另一个容器具有 25%的可用份额。这些数字是由于 CPU 共享的加权方法而不是固定容量。在第一个容器下方将允许拥有 50%的份额。第二个容器将限制在 25%。


docker run -d --name c768 --cpuset-cpus 0 --cpu-shares 768 benhall/stressdocker run -d --name c256 --cpuset-cpus 0 --cpu-shares 256 benhall/stresssleep 5docker stats --no-streamCONTAINER ID        NAME                CPU %               MEM USAGE / LIMIT   MEM %               NET I/O             BLOCK I/O           PIDS41fa6c06b148        c256                24.77%              736KiB / 737.6MiB   0.10%               2.1kB / 180B        0B / 0B             34555c9a0c612        c768                74.33%              732KiB / 737.6MiB   0.10%               2.19kB / 484B       0B / 0B             3da4db4fd6b70        mb100               0.00%               444KiB / 100MiB     0.43%               12.7kB / 90B        1.06MB / 0B         1docker rm -f c768 c256
复制代码


有一点很重要,就是只要没有其他进程在,即便是定义了权重,启动的进程也能获得共享的 100%的资源。


4、其他限制

诸如读写 IP 的限制,可以按照参考文档配置测试,测试效果如上面的 cpu 和内存限制。


作者介绍


林伟壕,腾讯高级工程师,专注于企业 SDL、SecDevOps 建设。目前从事安全风险评估与代码审计,曾在国内大型电信运营商与顶尖游戏公司从事运维、安全体系建设工作。


原文链接


https://mp.weixin.qq.com/s?__biz=MzI4NTA1MDEwNg==&mid=2650779454&idx=2&sn=16ddb02eaea3c708bb23439feef077e8&chksm=f3f91aabc48e93bdb2ff5f40de779c010345c0842dff85f214e811e77ead6fe40230df69eae4&scene=27#wechat_redirect


2019 年 7 月 31 日 08:003927
用户头像
dbaplus社群 数据连接未来

发布了 156 篇内容, 共 41.5 次阅读, 收获喜欢 431 次。

关注

评论

发布
暂无评论
发现更多内容

架构师训练营 - 第四周作业

Mark

[编程参考-连载] Snowflake 算法原理与对应的 Python 实现

Python编程参考官方账号

Python 算法

android开发三大框架!国内一线互联网公司面试题汇总,终局之战

欢喜学安卓

android 程序员 面试 移动开发

武汉老旧小区改造平安智慧社区综合管理平台开发方案

WX13823153201

区块链数字钱包APP系统开发|区块链数字钱包软件开发

开發I852946OIIO

系统开发

区块链钱包APP系统开发|区块链钱包软件开发

开發I852946OIIO

系统开发

使用 AWS CDK Python 从零开始构建 EKS 集群

郭旭东

AWS IaC AWS CDK

PolarDB-X 并行计算框架

PolarDB-X

数据库 sql 大数据

2020下半年可信边缘云评估结果揭晓,2021年新一轮评估正式开启

浪潮云

大数据 可信云 可信边缘云

管理的亲和力是怎么练成的?

一笑

管理 沟通与管理 28天写作

循环?还是递归?

xcbeyond

Java 算法 递归 28天写作

使用 external version 进行 Elasticsearch 并发控制

escray

elastic 七日更 28天写作 死磕Elasticsearch 60天通过Elastic认证考试

文章类网站前端日期的显示该如何选择时区?

IT蜗壳-Tango

七日更 服务器时区

聊聊暴力递归 (一)

科比信徒

Java 算法

大数据场景下Volcano高效调度能力实践

华为云开发者社区

大数据 spark Kubernetes Volcano application

MySQL 5.6.35 索引优化导致的死锁案例解析

vivo互联网技术

MySQL 数据库 死锁

当公元成了可以考古的年代「幻想短篇 17/28」

道伟

28天写作

数据库性能调优之始: analyze统计信息

华为云开发者社区

数据库 sql GaussDB 语义

企业项目迁移go-zero全攻略(一)

Kevin Wan

go 微服务 微服务架构 微服务治理 microservice

KubeEdge和Kuiper“双剑合并”,轻松解决边缘流式数据处理

华为云开发者社区

spark 边缘计算 kuberedge kuiper 边缘流式数据

字节跳动&火山引擎:企业级机器学习平台建设实践

火山引擎

机器学习 云计算 AI 云原生

Redis为什么变慢了?一文讲透如何排查Redis性能问题 | 万字长文

Kaito

redis 性能优化 后端

与前端训练营的日子 --Week13

SamGo

学习笔记

作业2

瑾瑾呀

OpsMind 前端低代码开发平台——MPlatform

OpsMind

前端 低代码

年会游戏:猜数字(前端特效)

学习委员

CSS html 前端 js 28天写作

第二章作业(一)

LouisN

Why me, why now Jan 25, 2021

王泰

28天写作

生活,在哪里都一样

熊斌

个人成长 28天写作

鸿蒙开发者beta!Github标星25K+超火的Android实战项目,赶紧收藏!

欢喜学安卓

android 程序员 面试 移动开发

聊聊 Git 的三种传输协议及实现

Zoker

git 架构 DevOps

2021 ThoughtWorks 技术雷达峰会

2021 ThoughtWorks 技术雷达峰会

还没对Docker加以限制?埋下的安全隐患了解一下-InfoQ