Docker 安全評估與控制

docker 容器的安全很大程度上依賴 linux 本身,因爲是共享宿主機內核。

docker 安全評估主要考慮以下幾個方面:

  1. linux 內核的命名空間(namespace)機制提供的容器隔離安全
  2. linux 控制組(cgroup)對容器資源的控制能力安全
  3. linux 內核的能力機制所帶來的操作系統安全
  4. docker 程序(主要是服務器端)本身的抗攻擊能力
  5. 其他安全增強機制的影響

Docker安全評估

  1. 命名空間隔離安全: docker run 啓動一個容器時,後臺會爲容器創建一個獨立的命名空間,
    這是最基礎的隔離,讓容器作爲一個獨立的個體存在
[root@server1 ~]# docker run -it --name vm1 ubuntu	
root@ed7c89bbbfe3:/#		# ctrl + p + q 退出,讓容器保持運行
 [root@server1 ~]# docker inspect vm1 |grep Pid		# 容器其實就是一個進程,查看他的pid
            "Pid": 5279,			
            "PidMode": "",
            "PidsLimit": 0,
For more details see ps(1).
[root@server1 ~]# ps ax | grep 5279
 5279 pts/0    Ss+    0:00 /bin/bash			# 這時5279這個進程打開的bash
 5346 pts/0    R+     0:00 grep --color=auto 5279
 [root@server1 ~]# cd /proc/5279/ns/		## 進入它的命名空間
[root@server1 ns]# ls
ipc  mnt  net  pid  user  uts			# 這裏就是命名空間中的內容
  • 但是這種方式與傳統虛擬機相比,隔離的不徹底,因爲容器就是一個進程,那麼多個容器就還 是共用宿主機的內核
  • 在 linux 內核中,有些資源和對象不能被 namespace 化,如:時間,這樣機不能保證完全隔離了
  1. 控制組資源控制安全: docker run 啓動一個容器時,後臺會爲容器創建一個獨立的控制組
    策略集合
[root@server1 ns]# mount -t cgroup
cgroup on /sys/fs/cgroup/systemd type cgroup (rw,nosuid,nodev,noexec,relatime,xattr,release_agent=/usr/lib/systemd/systemd-cgroups-agent,name=systemd)
cgroup on /sys/fs/cgroup/net_cls,net_prio type cgroup (rw,nosuid,nodev,noexec,relatime,net_prio,net_cls)
cgroup on /sys/fs/cgroup/cpu,cpuacct type cgroup (rw,nosuid,nodev,noexec,relatime,cpuacct,cpu)
cgroup on /sys/fs/cgroup/perf_event type cgroup (rw,nosuid,nodev,noexec,relatime,perf_event)
cgroup on /sys/fs/cgroup/hugetlb type cgroup (rw,nosuid,nodev,noexec,relatime,hugetlb)
cgroup on /sys/fs/cgroup/devices type cgroup (rw,nosuid,nodev,noexec,relatime,devices)
cgroup on /sys/fs/cgroup/pids type cgroup (rw,nosuid,nodev,noexec,relatime,pids)
cgroup on /sys/fs/cgroup/memory type cgroup (rw,nosuid,nodev,noexec,relatime,memory)
cgroup on /sys/fs/cgroup/blkio type cgroup (rw,nosuid,nodev,noexec,relatime,blkio)
cgroup on /sys/fs/cgroup/freezer type cgroup (rw,nosuid,nodev,noexec,relatime,freezer)
cgroup on /sys/fs/cgroup/cpuset type cgroup (rw,nosuid,nodev,noexec,relatime,cpuset)
## 可以看到控制組的策略集合,

[root@server1 ~]# cd /sys/fs/cgroup/cpu/docker/
[root@server1 docker]# ls
cgroup.clone_children  cgroup.procs  cpuacct.usage         cpu.cfs_period_us  cpu.rt_period_us   cpu.shares  ed7c89bbbfe3ac5c6efef492b919eef7b108e655964b3e956ce197201a696e7e  tasks
cgroup.event_control   cpuacct.stat  cpuacct.usage_percpu  cpu.cfs_quota_us   cpu.rt_runtime_us  cpu.stat    notify_on_release
## 可以看到 cpu 資源控制組爲 docker 容器分配的 cpu 資源
裏面的 ed7c89bbbfe3ac5c6efef492b919eef7b108e655964b3e956ce197201a696e7e 就是我們剛纔啓動的一個容器隨機生成的.

[root@server1 ed7c89bbbfe3ac5c6efef492b919eef7b108e655964b3e956ce197201a696e7e]# ls
cgroup.clone_children  cgroup.procs  cpuacct.usage         cpu.cfs_period_us  cpu.rt_period_us   cpu.shares  notify_on_release
cgroup.event_control   cpuacct.stat  cpuacct.usage_percpu  cpu.cfs_quota_us   cpu.rt_runtime_us  cpu.stat    tasks
而且裏面的參數時完全繼承上機目錄的,也就係統中的資源
  • linux cgroup 還提供了很多有用特性,確保容器可以公平分享主機內存、cpu 等資源
  • 確保當容器內資源壓力不會影響到宿主機和其他容器,在 DDos 方面必不可少
  1. 內核能力機制 : 是 linux 內核的一個強大特性,可提供細粒度的權限訪問控制
    大部分情況下,容器並不需要真正的 root 權限,只要少數能力即可
[root@server1 ~]# docker attach vm1			# 連上剛纔的容器
root@ed7c89bbbfe3:/# id
uid=0(root) gid=0(root) groups=0(root)				# 看出是超級用戶,但是沒有超戶的權力

root@ed7c89bbbfe3:/# ip addr		
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default 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
10: eth0@if11: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default 
    link/ether 02:42:ac:11:00:02 brd ff:ff:ff:ff:ff:ff
    inet 172.17.0.2/16 brd 172.17.255.255 scope global eth0
       valid_lft forever preferred_lft forever
       
root@ed7c89bbbfe3:/# ip link set down eth0
RTNETLINK answers: Operation not permitted		#但是想要關閉網卡,不行,雖然是 root

  1. docker 服務端的防護:確保只有可信的用戶才能訪問到 docker 服務;將容器的 root 用戶映射到本地主機的非 root 用戶,減輕容器和主機之間因權限提升而引起的安全問題;允許docker 服務端在非 root 權限下運行,利用安全可靠的子進程來代理執行需要特權的操作(子進程只允許在特定範圍內操作)

  2. 其他安全特性:使用有增強安全特性的容器模板;用戶可以定義更加嚴格的訪問控制機制等
    (比如文件系統掛載到容器內部時,設置只讀)

容器資源控制

  • linux Cgroups:組資源控制是限制一個進程組使用資源的上限,包括 cpu、內存、磁盤、帶寬等
[root@server1 ~]# mount -t cgroup
cgroup on /sys/fs/cgroup/systemd type cgroup /lib/systemd/systemd-cgroups-agent,name=systemd)
cgroup on /sys/fs/cgroup/net_cls,net_prio type cgroup 
cgroup on /sys/fs/cgroup/cpu,cpuacct type cgroup 
cgroup on /sys/fs/cgroup/perf_event type cgroup
cgroup on /sys/fs/cgroup/hugetlb type cgroup 
cgroup on /sys/fs/cgroup/devices type cgroup 
cgroup on /sys/fs/cgroup/pids type cgroup 
cgroup on /sys/fs/cgroup/memory type cgroup 
cgroup on /sys/fs/cgroup/blkio type cgroup 
cgroup on /sys/fs/cgroup/freezer type cgroup 
cgroup on /sys/fs/cgroup/cpuset type cgroup 
## 看到都掛載在/sys/fs/cgroup 下
[root@server1 ~]# cat /etc/security/limits.conf
## 我們在這裏也可以設置進程數,內存等,但是不是很準確,因爲還有 swap 分區,swap 分區也是可以使用的。

所以我們可以在 docker 啓動是指定使用的內存,cpu 等:

cpu限制

## 使用 docker run --cpu  --memory	進行指定就可以了

那我們如何在系統中進行設定那:

[root@server1 ~]# cd /sys/fs/cgroup/
[root@server1 cgroup]# ls
blkio  cpu  cpuacct  cpu,cpuacct  cpuset  devices  freezer  hugetlb  memory  net_cls  net_cls,net_prio  net_prio  perf_event  pids  systemd
# 這個目錄下都是限制系統各項內容的,有磁盤IO,cpu 內存,設備等。
裏面的子目錄也叫子系統。這裏面每個子系統都有docker容器的目錄。

我們可以在在每個子系統下,爲每個進程創建一個控制組。
[root@server1 cpu]# mkdir a1
[root@server1 cpu]# ls a1/
cgroup.clone_children  cgroup.procs  cpuacct.usage         cpu.cfs_period_us  cpu.rt_period_us   cpu.shares  notify_on_release
cgroup.event_control   cpuacct.stat  cpuacct.usage_percpu  cpu.cfs_quota_us   cpu.rt_runtime_us  cpu.stat    tasks
[root@server1 cpu]# cat cpu.rt_period_us 
1000000
[root@server1 cpu]# cat a1/cpu.rt_period_us 
1000000
# 我們在cpu子系統下建立一個控制組,可以看出子控制器和父級值是一樣的,父級是針對所有控制器,子控制器的值可修改,針對某進程。

我們現在在操作系統層面控制(也可以在容器內)

[root@server1 cpu]# yum install libcgroup-tools.x86_64 -y  #安裝 cgroup 命令行工具
[root@server1 cpu]# cat cpu.cfs_period_us
100000				# cpu 調用週期,單位:微秒
[root@server1 cpu]# cat cpu.cfs_quota_us
-1					# 配額限制,-1 表示不限制
[root@server1 cpu]# cd a1/
[root@server1 a1]# echo 20000  > cpu.cfs_quota_us 		# 表示 100000 微秒的週期內,只能使用 20000,也就是 20%
[root@server1 a1]# cat cpu.cfs_quota_us 
20000

驗證:
[root@server1 a1]# dd if=/dev/zero of=/dev/null &
[1] 5796
# 這是一個無限循環的進程,不會佔用磁盤和 IO,只會消耗 cpu

在這裏插入圖片描述
我們發現佔用了幾乎100的cpu,這是因爲我們並沒有將這個進程和這個限制關聯起來,
所以:

[root@server1 a1]# echo 5796 > tasks
# 讓進程 id 和 tasks 關聯,tasks 裏面是那個進程,這個資源組就控制的誰

再次top查看:
在這裏插入圖片描述
這次就限制到了20%。

那末我們在手動指定容器試一下:

[root@server1 a1]# docker run -it --name vm2 --cpu-period 100000 --cpu-quota 20000 ubuntu
## 指定配額爲20000 CTRL +P+Q 先退出,在docker資源組中查看:

[root@server1 a1]# cd ../docker/
[root@server1 docker]# cd fc8692dd94d170ff83dd274e28049eb68a617733e91dda6145a171541b535101/
## 進入我們的容器VM2的目錄
[root@server1 fc8692dd94d170ff83dd274e28049eb68a617733e91dda6145a171541b535101]# ls
cgroup.clone_children  cgroup.procs  cpuacct.usage         cpu.cfs_period_us  cpu.rt_period_us   cpu.shares  notify_on_release
cgroup.event_control   cpuacct.stat  cpuacct.usage_percpu  cpu.cfs_quota_us   cpu.rt_runtime_us  cpu.stat    tasks
[root@server1 fc8692dd94d170ff83dd274e28049eb68a617733e91dda6145a171541b535101]# cat cpu.cfs_quota_us 
20000
查看,卻是是20000,所以對容器的限制就是我們在開啓是加上參數就行了

我們再再容器中用dd 做測試:

[root@server1 cpu]# docker attach vm2
root@fc8692dd94d1:/# dd if=/dev/zero of=/dev/null &
[1] 15
## ctrl +p +q 退出

在這裏插入圖片描述
可以看到,對容器裏面的進程也是其效果的,和剛纔在命令行中的dd 一樣,都只佔用了20%的cpu。

內存限制

容器可用內存包括:物理內存、swap 分區(操作系統也是)。

  • 但是一旦切換到 swap 分區,性能就不能保證了,因爲 swap 是物理硬盤,當然沒有內存快’
  • 容器的資源限制很簡單 docker run -it --memory 256M --memory-swap=256M ubuntu 就可以了

操作系統層面的限制:

[root@server1 memory]# cd /sys/fs/cgroup/memory/
[root@server1 memory]# mkdir a2
[root@server1 memory]# cd a2
[root@server1 a2]# ls
cgroup.clone_children  memory.kmem.failcnt             memory.kmem.tcp.limit_in_bytes      memory.max_usage_in_bytes        memory.move_charge_at_immigrate  memory.stat            tasks
cgroup.event_control   memory.kmem.limit_in_bytes      memory.kmem.tcp.max_usage_in_bytes  memory.memsw.failcnt             memory.numa_stat                 memory.swappiness
cgroup.procs           memory.kmem.max_usage_in_bytes  memory.kmem.tcp.usage_in_bytes      memory.memsw.limit_in_bytes      memory.oom_control               memory.usage_in_bytes
memory.failcnt         memory.kmem.slabinfo            memory.kmem.usage_in_bytes          memory.memsw.max_usage_in_bytes  memory.pressure_level            memory.use_hierarchy
memory.force_empty     memory.kmem.tcp.failcnt         memory.limit_in_bytes               memory.memsw.usage_in_bytes      memory.soft_limit_in_bytes       notify_on_release
[root@server1 a2]# cat memory.limit_in_bytes 		# 這裏是內存的最大限制,默認是不限制的
9223372036854771712

那我們限制最大隻能使用256M:	256*1024*1024=268435456
[root@server1 a2]# echo 268435456 > memory.limit_in_bytes
[root@server1 a2]# cat memory.limit_in_bytes 
268435456

測試:

[root@server1 shm]# df -H
Filesystem             Size  Used Avail Use% Mounted on
/dev/mapper/rhel-root   19G  3.0G   16G  16% /
devtmpfs               508M     0  508M   0% /dev
tmpfs                  520M     0  520M   0% /dev/shm
#這個目錄掛載的是內存的 1/2,當前主機是 1G 內存,那麼這裏就是 512M,我們用它佔用內存
[root@server1 shm]# free -m
              total        used        free      shared  buff/cache   available
Mem:            991         131         356          12         502         689		# 內存當前可用爲689M
Swap:          2047           0        2047					# swap 分區爲2047M

進行截取:
[root@server1 shm]# cd /dev/shm/		# 進入這個目錄
[root@server1 shm]# ls
[root@server1 shm]# dd if=/dev/zero of=bigfile bs=1M count=100		# 截取100M
100+0 records in
100+0 records out
104857600 bytes (105 MB) copied, 0.0557332 s, 1.9 GB/s
[root@server1 shm]# ls
bigfile
[root@server1 shm]# free -m
              total        used        free      shared  buff/cache   available
Mem:            991         129         258         112         603         592	#確實少了100
Swap:          2047           0        2047
[root@server1 shm]# dd if=/dev/zero of=bigfile bs=1M count=200
200+0 records in
200+0 records out
209715200 bytes (210 MB) copied, 0.125939 s, 1.7 GB/s		# 截取200M
[root@server1 shm]# free -m
              total        used        free      shared  buff/cache   available
Mem:            991         129         158         212         703         491
Swap:          2047           0        2047
[root@server1 shm]# dd if=/dev/zero of=bigfile bs=1M count=300		# 截取300M
300+0 records in
300+0 records out
314572800 bytes (315 MB) copied, 0.164207 s, 1.9 GB/s
[root@server1 shm]# free -m
              total        used        free      shared  buff/cache   available
Mem:            991         130          70         312         790         392
Swap:          2047           1        2046

## 我們發現,剛剛明明設置了256M,這裏卻300M都可以使用了,
還是因爲我們沒有進行綁定。

##剛纔裝的 cgexec 命令,可以直接在命令行控制資源組
[root@server1 shm]# cgexec -g memory:a2 dd if=/dev/zero of=bigfile bs=1M
## memory和a2這個控制器進行綁定

[root@server1 shm]# free -m
              total        used        free      shared  buff/cache   available
Mem:            991         129         270         112         590         591	# 少了100M
Swap:          2047           0        2047				
[root@server1 shm]# cgexec -g memory:a2 dd if=/dev/zero of=bigfile bs=1M count=300
300+0 records in														# 截取300M
300+0 records out	
314572800 bytes (315 MB) copied, 0.185631 s, 1.7 GB/s
[root@server1 shm]# free -m
              total        used        free      shared  buff/cache   available
Mem:            991         130         115         266         744         436	# 這裏至少了253 M 確實限制再了256M之內
Swap:          2047          46        2001
[root@server1 shm]# ls
bigfile

但是這樣的文件竟然創建成功了,並沒有報錯,而且我們發現swap分區少了36M,

這就說明:多出去的不能使用內存,所以使用了 swap,那我們怎麼徹底限制哪?

[root@server1 shm]# cd /sys/fs/cgroup/memory/a2		
[root@server1 a2]# cat memory.memsw.limit_in_bytes 
9223372036854771712		# 我們發現swap分也是沒有被限制的
[root@server1 a2]# echo 268435456 > memory.memsw.limit_in_bytes 		# 也限制爲256M
-bash: echo: write error: Device or resource busy		# 失敗時因爲我們剛纔使用到了swap分區
[root@server1 a2]# rm -fr /dev/shm/bigfile 
[root@server1 a2]# echo 268435456 > memory.memsw.limit_in_bytes 
## 現在就可以了。改完後表示物理內存和 swap 一共使用不能超過 256M

#測試:
[root@server1 shm]# cgexec -g memory:a2 dd if=/dev/zero of=bigfile bs=1M count=300
Killed
發現這個就被殺掉了,說明限制成功

block io限制,對磁盤讀寫的限制

[root@server1 shm]# cd /sys/fs/cgroup/blkio/
[root@server1 blkio]# mkdir a3
[root@server1 blkio]# cd a3/
[root@server1 a3]# ls
blkio.io_merged            blkio.io_service_bytes_recursive  blkio.io_wait_time            blkio.sectors                    blkio.throttle.read_iops_device   blkio.weight           notify_on_release
blkio.io_merged_recursive  blkio.io_serviced                 blkio.io_wait_time_recursive  blkio.sectors_recursive          blkio.throttle.write_bps_device   blkio.weight_device    tasks
blkio.io_queued            blkio.io_serviced_recursive       blkio.leaf_weight             blkio.throttle.io_service_bytes  blkio.throttle.write_iops_device  cgroup.clone_children
blkio.io_queued_recursive  blkio.io_service_time             blkio.leaf_weight_device      blkio.throttle.io_serviced       blkio.time                        cgroup.event_control
blkio.io_service_bytes     blkio.io_service_time_recursive   blkio.reset_stats             blkio.throttle.read_bps_device   blkio.time_recursive              cgroup.procs

# blkio.throttle.read_bps_device		每秒讀的數據量
# blkio.throttle.read_iops_device 		每秒的 io 操作次數

## '設置每秒寫入數據量爲 1M'
[root@server1 a3]# fdisk  -l
   Device Boot      Start         End      Blocks   Id  System
/dev/sda1   *        2048     2099199     1048576   83  Linux				
/dev/sda2         2099200    41943039    19921920   8e  Linux LVM
# 我們當前使用的磁盤是 /dev/sda
[root@server1 a3]# ll /dev/sda
brw-rw---- 1 root disk 8, 0 May 27 11:37 /dev/sda		##看到磁盤的主號和輔號(8 和 0)

[root@server1 a3]# echo "8:0 1048576" > blkio.throttle.write_bps_device
#1024 * 1024 = 1048576
[root@server1 ~]# cgexec -g blkio:a3 dd if=/dev/zero of=testfile bs=1M count=10
10+0 records in
10+0 records out
10485760 bytes (10 MB) copied, 0.00391746 s, 2.7 GB/s
## 沒有生效,速率爲2.7G/s
這是因爲目前 block io 限制只對 direct io 有效(不使用文件緩存)

[root@server1 ~]# cgexec -g blkio:a3 dd if=/dev/zero of=testfile bs=1M count=10 oflag=direct
10+0 records in
10+0 records out
10485760 bytes (10 MB) copied, 10.0016 s, 1.0 MB/s	# 生效了,耗時10s

那我們docker中怎樣去限制哪?

在使用容器時也有相關參數,我們在使用時直接docker run 加上參數就好了:
[root@server1 a3]# docker run --help |grep device
      --blkio-weight-device list       Block IO weight (relative device weight) (default [])
      --device list                    Add a host device to the container
      --device-cgroup-rule list        Add a rule to the cgroup allowed devices list
      --device-read-bps list           Limit read rate (bytes per second) from a device (default [])
      --device-read-iops list          Limit read rate (IO per second) from a device (default [])
      --device-write-bps list          Limit write rate (bytes per second) to a device (default [])
      --device-write-iops list         Limit write rate (IO per second) to a device (default [])
[root@server1 ~]# docker run -it --name vm1 --device-write-bps /dev/sda:1MB ubuntu		#創建容器
root@36784d59d0f5:/# dd if=/dev/zero of=testfile bs=1M count=10 oflag=direct			# 截取文件
10+0 records in
10+0 records out
10485760 bytes (10 MB) copied, 10.2338 s, 1.0 MB/s		# 限制生效了
## ctrl + p + q 退出
# 那我們去操作系統層面去查看
[root@server1 ~]# cd /sys/fs/cgroup/blkio/docker/
[root@server1 docker]# cd 36784d59d0f5841ba3c22be228e97aa537f87eb39915a7568046bf7151c04139/
[root@server1 36784d59d0f5841ba3c22be228e97aa537f87eb39915a7568046bf7151c04139]# cat blkio.throttle.write_bps_device 
8:0 1048576		# 和之前手動寫入到 a3 中的一樣

其它限制

[root@server1 ~]# cd /sys/fs/cgroup/freezer 		# 管控系統進程暫停與恢復的
[root@server1 freezer]# cd docker/36784d59d0f5841ba3c22be228e97aa537f87eb39915a7568046bf7151c04139/
[root@server1 36784d59d0f5841ba3c22be228e97aa537f87eb39915a7568046bf7151c04139]# cat tasks 		
6485			## 查看進程號
[root@server1 36784d59d0f5841ba3c22be228e97aa537f87eb39915a7568046bf7151c04139]# cat freezer.state 
THAWED				## 查看進程狀態,THAWED表示活躍的
[root@server1 36784d59d0f5841ba3c22be228e97aa537f87eb39915a7568046bf7151c04139]# docker pause vm1
vm1				## 凍結vm1
[root@server1 36784d59d0f5841ba3c22be228e97aa537f87eb39915a7568046bf7151c04139]# cat freezer.state 
FROZEN			## 狀態爲凍結
[root@server1 36784d59d0f5841ba3c22be228e97aa537f87eb39915a7568046bf7151c04139]# docker attach vm1
You cannot attach to a paused container, unpause it first			## 嘗試連接被拒絕
[root@server1 36784d59d0f5841ba3c22be228e97aa537f87eb39915a7568046bf7151c04139]# cat tasks 
6485				## 但是進程依然存在
[root@server1 36784d59d0f5841ba3c22be228e97aa537f87eb39915a7568046bf7151c04139]# ps ax
 6485 pts/0    Ds+    0:00 /bin/bash		## D 表示凍結,暫停
[root@server1 36784d59d0f5841ba3c22be228e97aa537f87eb39915a7568046bf7151c04139]# docker unpause vm1 
vm1 
[root@server1 36784d59d0f5841ba3c22be228e97aa537f87eb39915a7568046bf7151c04139]# cat freezer.state 
THAWED
[root@server1 36784d59d0f5841ba3c22be228e97aa537f87eb39915a7568046bf7151c04139]# ps ax |grep bash
 6485 pts/0    Ss+    0:00 /bin/bash
[root@server1 36784d59d0f5841ba3c22be228e97aa537f87eb39915a7568046bf7151c04139]# docker attach vm1
root@36784d59d0f5:/#  		# 就可以連接了,然後退出
[root@server1 ~]# docker container prune 		## 刪除所有退出的容器
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章