Docker安全

前言

docker内部的隔离机制是通过namespace来做的,但是在Linux系统里面很多东西是不能通过namespace来隔离的,比如:时间。
容器的资源控制,而namespace把容器在我们的linux系统网络成为独立个体,并不能做资源限制,很可能一个容器上面跑了一个java程序,但是这个容器把我们的内存占用完了,那么这是不合理的。
所以一定要对docker容器的配额进行限额,资源限制。

docker内部通过Cgroup直接进行资源限制,我们可以在run建立容器时加参数进行控制。

Docker容器的安全性,很大程度上依赖于Linux系统自身,评估Docker的安全性时,主要考虑以下几个方面:

  • Linux内核的命名空间机制提供的容器隔离安全
  • Linux控制组机制对容器资源的控制能力安全。
  • Linux内核的能力机制所带来的操作权限安全
  • Docker程序(特别是服务端)本身的抗攻击性。
  • 其他安全增强机制对容器安全性的影响。

命名空间隔离的安全

  • 当docker run启动一个容器时,Docker将在后台为容器创建一个独立的命名空间。命名空间提供了最基础也最直接的隔离。
  • 与虚拟机方式相比,通过Linux namespace来实现的隔离不是那么彻底。
  • 容器只是运行在宿主机上的一种特殊的进程,那么多个容器之间使用的就还是同一个宿主机的操作系统内核。
  • 在 Linux 内核中,有很多资源和对象是不能被 Namespace 化的,比如:时间

控制组资源控制的安全

  • 当docker run启动一个容器时,Docker将在后台为容器创建一个独立的控制组策略集合。
  • Linux Cgroups提供了很多有用的特性,确保各容器可以公平地分享主机的内存、CPU、磁盘IO等资源。
  • 确保当发生在容器内的资源压力不会影响到本地主机系统和其他
  • 容器,它在防止拒绝服务攻击(DDoS)方面必不可少。

今天我们主要讲一下cgroup机制对容器资源的控制能力。
首先了解一下几个控制器:

  1. blkio: 这个subsystem可以为块设备设定输入/输出限制,比如物理驱动设备(包括磁盘、固态硬盘、USB等)。
  2. cpu: 这个subsystem使用调度程序控制task对CPU的使用。
  3. cpuacct: 这个subsystem自动生成cgroup中task对CPU资源使用情况的报告。
  4. cpuset: 这个subsystem可以为cgroup中的task分配独立的CPU(此处针对多处理器系统)和内存
  5. devices: 这个subsystem可以开启或关闭cgroup中task对设备的访问。
  6. freezer: 这个subsystem可以挂起或恢复cgroup中的task。
  7. memory: 这个subsystem可以设定cgroup中task对内存使用量的限定,并且自动生成这些task对内存资源使用情况的报告
  8. perf_event: 这个subsystem使用后使得cgroup中的task可以进行统一的性能测试。
  9. net_cls: 这个subsystem Docker没有直接使用,它通过使用等级识别符(classid)标记网络数据包,从而允许 Linux 流量。

安全–设置内存+交换分区

一.针对系统中的某个进程
1.安装cgroup,可提供cgexec命令

[root@server1 ~]# yum install libcgroup-tools.x86_64 -y
[root@server1 ~]# mount -t cgroup
[root@server1 ~]# cd /sys/fs/cgroup/memory/
[root@server1 memory]# cat memory.limit_in_bytes 
9223372036854771712		数字太大,等同于没有做限制
[root@server1 memory]# cat memory.memsw.limit_in_bytes 
9223372036854771712

在这里插入图片描述
2.设定资源限制参数:内存+交换分区<=200M

[root@server1 ~]# cd /sys/fs/cgroup/memory/
[root@server1 memory]# mkdir ljz	创建目录ljz,该目录的名字随意给。在/sys/fs/cgroup/memory目录中创建的目录,自动继承/sys/fs/cgroup/memory目录中的内容。
创建该目录的目的是(1)为了演示容器的运行过程。因为一旦运行容器,就会在该目录下,生成一个docker目录,docker目录中会生成容器ID对应的目录,目录中memory目录下的内容继承于/sys/fs/cgroup/memeory目录下的内容。(2)直接修改/sys/fs/cgroup/memory中的文件的内容,会报错。
[root@server1 memory]# echo 209715200 > memory.limit_in_bytes 
-bash: echo: write error: Invalid argument
[root@server1 memory]# cd ljz/
[root@server1 ljz]# echo 209715200 > memory.limit_in_bytes 	#设定最大占用内存为200M(209715200=200*1024*1024。209715200的单位为BB)
[root@server1 ljz]# echo 209715200 > memory.memsw.limit_in_bytes	#因为最大占用内存数和最大占	用swap分区的内存数一样。表明最大可用内存为200M,可用swap为0M。即限制了内存+交换分区<=200M
[root@server1 xin]# cat memory.limit_in_bytes   
[root@server1 ljz]# cat memory.limit_in_bytes 
209715200
[root@server1 ljz]# cat memory.memsw.limit_in_bytes 
209715200
值的注意的是:/sys/fs/cgroup/memory目录中的文件,不能用vim进行编辑,利用vim进行编辑,无法进行保存退出(即使使用"wq!",也不能保存退出。)

在这里插入图片描述
现在的内存可用为3382M

3.测试

[root@server1 ~]# cd /dev/shm/	#在该目录下进行操作,相当于直接占用内存。
[root@server1 shm]# cgexec -g memory:ljz dd if=/dev/zero of=bigfile bs=1M count=100
100+0 records in
100+0 records out
104857600 bytes (105 MB) copied, 0.0386729 s, 2.7 GB/s
[root@server1 shm]# free -m
              total        used        free      shared  buff/cache   available
Mem:           3791         158        2829         116         803        3282
Swap:           499           0         499 	我们发现可用内存少了100M
[root@server1 shm]# cgexec -g memory:ljz dd if=/dev/zero of=bigfile bs=1M count=300
Killed
#因为指定的文件的大小为300M超过了限制,所以显示Killed,这就是之前我们为ljz目录设置的限制,其最多只能占用200M
[root@server1 shm]# free -m
              total        used        free      shared  buff/cache   available
Mem:           3791         157        2730         215         902        3184
Swap:           499           0         499
多余的部分就无法占用了

补充

  • 如果不对内存和swap分区进行限制,即不修改/sys/fs/cgroup/memory/memory.limit_in_bytes和/sys/fs/cgroup/memory/memory.memsw.limit_in_bytes文件中的内容。那么不管要生成的bigfile文件的大小为多少,dd命令永远会成功。

    扫描二维码关注公众号,回复: 6455054 查看本文章
  • 如果只是对内存进行限制(限制为200M),而没有对交换f分区进行限制,即只修改/sys/fs/cgroup/memory/memory.limit_in_bytes文件中的内容,而并没有修改/sys/fs/cgroup/memory/memory.memsw.limit_in_bytes文件中的内容。那么如果要生成的bigfile文件的大小大于200M,dd命令会成功,但是只有200M是取自内存,剩余的取自交换分区。

二、针对docker设置内存和交换分区大小,运行容器
1.指定内存和交换分区的大小,运行容器

[root@server1 ~]# docker run -it --name vm1 --memory 209715200 --memory-swap 209715200 ubuntu
root@f00384964902:/# [root@server1 ~]# 
利用ubuntu镜像运行容器vm1,指定内存+交换分区<200M。并使用Ctrl+p+q退出,即不要让容器停掉。

2.查看设置是否生效

在这里插入图片描述
这是我们查看到的内存和交换分区大小符合我们设置的大小,那么我们确认一下pid号,以确保我们查看的确实是我们运行的容器
在这里插入图片描述
在这里插入图片描述

值的注意的是:因为容器的隔离性并不是很好,所以在容器内使用命令"free -m"看到的内容与宿主机上使用命令"free -m"看到的内容相同。所以如果要看是否限制成功,需要进入容器对应的目录中进行查看。
补充

  • –memory可以单独使用,但是–memory-swap是必须要与–memory一起使用的
  • 正常情况下,–memory-swap的值包含容器可用内存和可用swap,所以–memory=“300m” --memory-swap="1g"的含义为:容器可以使用300M的物理内存,并且可以使用700M(1G-300M)的swap。–memory-swap 是容器可以使用的物理内存和可以使用的swap之和!
    把–memory-swap设置为0和不设置是一样的,此时如果设置了–memory,则容器可以使用两倍–memory容量的swap。–memory=“300m”,–memory-swap没有设置,相当于可以使用300m内存+600m的swap
    如果–memory-swap的值和–memory相同,则容器不能使用swap。
    如果–memory-swap设置为-1,相当于不限制swap的容量,但是会受host主机的swap容量
    在容器内部,free看到的swap是host的swap,并不是容器可用的swap容量。
    –oom-kill-disable 当OOM(Out Of Memory)发生的时候,内核会kill掉容器内的进程,为了改变这种行为,可以在设置–memory的时候,同时设置–oom-kill-disable。如果没有设置–oom-kill-disable,则host可能会OOM,这时候内核会kill host的系统进程来释放memory。

针对cpu的限制

一、针对某个系统进程
1.对cpu.cfs_quota_us写入整数值可以控制占用的cpu的大小

[root@server1 ~]# cd /sys/fs/cgroup/cpu
[root@server1 cpu]# mkdir ljz2
[root@server1 cpu]# cd ljz2/
[root@server1 ljz2]# echo 20000 > cpu.cfs_quota_us 	#占用"20000/100000=20%"的CPU
[root@server1 ljz2]# cat cpu.cfs_quota_us 
20000	
同样/sys/fs/cgroup/cpu目录中的文件,不能用vim进行编辑,利用vim进行编辑,无法进行保存退出(即使使用"wq!",也不能保存退出。

2.测试

[root@server1 ljz2]# cd
[root@server1 ~]# dd if=/dev/zero of=/dev/null &
[root@server1 ~]# top		#看到dd命令占用的cpu比例将近为100%,这是因为还没有在tasks文件中写入要限制的进程号
 9448 root      20   0  107940    608    516 R 100.0  0.0   0:51.01 dd 

[root@server1 ~]# cd /sys/fs/cgroup/cpu/ljz2/
[root@server1 ljz2]# cat tasks 
[root@server1 ljz2]# echo 9448 > tasks 	#写入dd命令的进程号,对该进程进行限制
[root@server1 ljz2]# cat tasks 
9448
[root@server1 ljz2]# top
9448 root      20   0  107940    608    516 R  19.7  0.0   2:52.10 dd
[root@server1 ljz2]# kill -9 9448	#当dd命令对应的进程杀掉之后,tasks文件中的进程号,也会随之消失
[root@server1 ljz2]# cat tasks 
[root@server1 ljz2]# 

补充

  • 如果不对cpu进行限制,即不修改/sys/fs/cgroup/cpu/cpu.cfs_quota_us(文件内容默认是-1,表示不限制)文件中的内容。那么执行命令"dd if=/dev/zero of=/dev/null &"时,会占用100%的cpu。
  • cpu.cfs_period_us:cpu分配的周期(微秒),默认为100000。
  • cpu.cfs_quota_us:表示该control group限制占用的时间(微秒),默认为-1,表示不限制。如果设为50000,表示占用50000/100000=50%的CPU。

二、针对docker
1.指定占用的cpu的大小,运行容器

[root@server1 ljz2]# cd
[root@server1 ~]# docker run -it --name vm2 --cpu-quota 20000 ubuntu		#利用ubuntu镜像运行容器vm2,指定占用的cpu的大小,并使用Ctrl+p+q退出,即不要让容器停掉。
root@6ad39fd98ef6:/# dd if=/dev/zero of=/dev/null &	
[1] 15

2.测试

 [root@server1 ~]# top
 9544 root      20   0    4368    360    280 R  20.0  0.0   0:16.03 dd 
 [root@server1 ~]# cd /sys/fs/cgroup/cpu/
[root@server1 cpu]# cd docker/
[root@server1 docker]# ls
6ad39fd98ef6c3bf716ae75c2e24883ed9a43ed3888bbbc0de1a0d69f4e7333d
cgroup.clone_children
cgroup.event_control
cgroup.procs
cpuacct.stat
cpuacct.usage
cpuacct.usage_percpu
cpu.cfs_period_us
cpu.cfs_quota_us
cpu.rt_period_us
cpu.rt_runtime_us
cpu.shares
cpu.stat
f00384964902ab5f185676e6f009b1f0fa44c17922f6ed53b2de00f538bc3000
notify_on_release
tasks
[root@server1 docker]# docker ps
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
6ad39fd98ef6        ubuntu              "/bin/bash"         2 minutes ago       Up 2 minutes                            vm2
f00384964902        ubuntu              "/bin/bash"         20 minutes ago      Up 20 minutes                           vm1
[root@server1 docker]# cd 6ad39fd98ef6c3bf716ae75c2e24883ed9a43ed3888bbbc0de1a0d69f4e7333d/
[root@server1 6ad39fd98ef6c3bf716ae75c2e24883ed9a43ed3888bbbc0de1a0d69f4e7333d]# cat cpu.cfs_quota_us 
20000
[root@server1 6ad39fd98ef6c3bf716ae75c2e24883ed9a43ed3888bbbc0de1a0d69f4e7333d]# cat tasks 
9498
9544

查看vm2容器的pid
在这里插入图片描述
和tasks内容一致

如果在运行容器时,不对cpu进行限制,那么在容器内执行命令"dd if=/dev/zero of=/dev/null",cpu的占有率会接近100%

针对写速率限制

针对docker容器

1.赋予全部的权限运行容器(因为直接运行容器的话,是以普通用户的身份进行登录的,会没有权限),查看容器内的磁盘名称
首先不做任何操作运行容器:(普通用户身份)

[root@server1 ~]# docker run -it --name vm3 ubuntu
root@568cb93d694f:/# fdisk -l
root@568cb93d694f:/# 

全部的权限运行容器:
在这里插入图片描述
2.限制写入的速率,运行容器,并进行测试

[root@server1 ~]# docker run -it --rm --device-write-bps /dev/sda:20MB ubuntu	#以写入速率为20M/s的限制运行容器
root@001e71729b9a:/# dd if=/dev/zero of=file bs=1M count=300 oflag=direct	截取300M的名为file的文件。其中参数oflag=direct表示跳过内存缓存,直接存在磁盘中。必须加该参数,否则测试会失败。

300+0 records in
300+0 records in
300+0 records out
314572800 bytes (315 MB) copied, 16.9675 s, 18.5 MB/s 可以看到速率为18.5MB/S,大致等于20,成功

Block IO限制

我们首先查看我们的memory控制器是否存在:

[root@server1 ~]# cd /sys/fs/cgroup/memory/ljz/
[root@server1 ljz]# cat memory.limit_in_bytes 
209715200
[root@server1 ljz]# cat memory.memsw.limit_in_bytes 
209715200

建立一个测试用户

[root@server1 ljz]# useradd ljz

编辑文件,配置

[root@server1 ljz]# vim /etc/cgrules.conf 
[root@server1 ljz]# tail -n 1 /etc/cgrules.conf 
ljz	memory	ljz/
用户	内存		控制器

启动服务:

[root@server1 ljz]# systemctl start cgred.service 

测试

[root@server1 x1]# su - ljz
Last login: Wed Jun  5 19:53:45 CST 2019 on pts/0
[ljz@server1 ~]$ cd /dev/shm/
[ljz@server1 shm]$ dd if=/dev/zero of=bigfile bs=1M count=100
[ljz@server1 shm]$ dd if=/dev/zero of=bigfile bs=1M count=200
[ljz@server1 shm]$ du -h bigfile 
198M	bigfile

猜你喜欢

转载自blog.csdn.net/weixin_42446031/article/details/91477694