文章目录
Docker安全
docker 容器的安全很大程度上依赖 linux 本身,因为是共享宿主机内核。
docker 安全评估主要考虑以下几个方面:
- linux 内核的命名空间(namespace)机制提供的容器隔离安全
- linux 控制组(cgroup)对容器资源的控制能力安全
- linux 内核的能力机制所带来的操作系统安全
- docker 程序(主要是服务器端)本身的抗攻击能力
- 其他安全增强机制的影响
命名空间隔离安全
docker run 启动一个容器时,后台会为容器创建一个独立的命名空间:
这是最基础的隔离,让容器作为一个独立的个体存在
[root@server1 ~]# docker run -it --name vm1 busyboxplus
/ # [root@server1 ~]# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
92842335e0c1 busyboxplus "/bin/sh" 10 seconds ago Up 8 seconds vm1
[root@server1 ~]# docker inspect ^C
[root@server1 ~]# docker inspect vm1 | grep Pid
"Pid": 5044,
"PidMode": "",
"PidsLimit": null,
[root@server1 ~]# ps ax | grep 5044
5044 pts/0 Ss+ 0:00 /bin/sh
5211 pts/0 S+ 0:00 grep --color=auto 5044
[root@server1 ~]# cd /proc/5044/ns/ ## 这是5044的命名空间namespace
[root@server1 ns]# ls
ipc mnt net pid user uts
缺点: 在 linux 内核中,有些资源和对象不能被 namespace 化,如:时间,这样就 不能保证完全隔离了
控制组资源控制安全
docker run 启动一个容器时,后台会为容器创建一个独立的控制组策略集合
[root@server1 ~]# 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/perf_event type cgroup (rw,nosuid,nodev,noexec,relatime,perf_event)
cgroup on /sys/fs/cgroup/memory type cgroup (rw,nosuid,nodev,noexec,relatime,memory)
cgroup on /sys/fs/cgroup/cpuset type cgroup (rw,nosuid,nodev,noexec,relatime,cpuset)
cgroup on /sys/fs/cgroup/blkio type cgroup (rw,nosuid,nodev,noexec,relatime,blkio)
cgroup on /sys/fs/cgroup/pids type cgroup (rw,nosuid,nodev,noexec,relatime,pids)
cgroup on /sys/fs/cgroup/hugetlb type cgroup (rw,nosuid,nodev,noexec,relatime,hugetlb)
cgroup on /sys/fs/cgroup/freezer type cgroup (rw,nosuid,nodev,noexec,relatime,freezer)
cgroup on /sys/fs/cgroup/cpu,cpuacct type cgroup (rw,nosuid,nodev,noexec,relatime,cpuacct,cpu)
cgroup on /sys/fs/cgroup/devices type cgroup (rw,nosuid,nodev,noexec,relatime,devices)
[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
[root@server1 cgroup]# cd cpu/docker/
[root@server1 docker]# ls
92842335e0c16565d469d3fd1b1441c2b6958cde8c44fd1f7e9274c21fb8b828 cgroup.event_control cpuacct.stat cpuacct.usage_percpu cpu.cfs_quota_us cpu.rt_runtime_us cpu.stat tasks
cgroup.clone_children cgroup.procs cpuacct.usage cpu.cfs_period_us cpu.rt_period_us cpu.shares notify_on_release
[root@server1 docker]# cd 92842335e0c16565d469d3fd1b1441c2b6958cde8c44fd1f7e9274c21fb8b828/
[root@server1 92842335e0c16565d469d3fd1b1441c2b6958cde8c44fd1f7e9274c21fb8b828]# 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 保证了不会因为某个容器占用资源过多而影响到其他容器
内核能力机制
linux 内核的一个强大特性,可提供细粒度的权限访问控制
大部分情况下,容器并不需要真正的 root 权限,只要少数能力即可
[root@server1 ~]# docker attach vm1
/ # id
uid=0(root) gid=0(root) groups=10(wheel)
/ # ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue 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
6: eth0@if7: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue
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
/ # ip link set down eth0
ip: SIOCSIFFLAGS: Operation not permitted
即使是root 用户也不能关闭网卡
docker 服务端的防护
确保只有可信的用户才能访问到 docker 服务;将容器的 root 用户映射到本地主机的非 root 用户,减轻容器和主机之间因权限提升而引起的安全问题;允许docker 服务端在非 root 权限下运行,利用安全可靠的子进程来代理执行需要特权的操作(子进程只允许在特定范围内操作)
其他安全特性
使用有增强安全特性的容器模板;用户可以定义更加严格的访问控制机制等
(比如文件系统挂载到容器内部时,设置只读)
容器资源控制
操作系统层面控制
yum install libcgroup-tools.x86_64 -y ### 安装cgroup控制工具
启动一个dd进程 只消耗cpu
dd if=/dev/zero of=/dev/null &
在/sys/fs/cgroup/cpu新建目录a1
[root@server1 a1]# pwd
/sys/fs/cgroup/cpu/a1
[root@server1 a1]# echo 20000 > cpu.cfs_quota_us
让进程id和tasks关联 (3686为dd进程Pid)
[root@server1 a1]# echo 3686 > tasks
容器层面控制
限制容器使用内存:
docker run --help
-m, --memory bytes Memory limit
--memory-reservation bytes Memory soft limit
--memory-swap bytes Swap limit equal to memory plus swap: '-1' to enable unlimited swap
--memory-swappiness int Tune container memory swappiness (0 to 100) (default -1)
[root@server1 ~]# docker run -it --name test --memory 50M --memory-swap 50M busybox
/ # cd /dev/shm/
/dev/shm # touch bigfile
/dev/shm # free -m
total used free shared buff/cache available
Mem: 990 195 428 0 367 654
Swap: 2047 0 2047
/dev/shm # dd if=/dev/zero of=bigfile bs=1M count=50
Killed
/dev/shm # dd if=/dev/zero of=bigfile bs=1M count=40
40+0 records in
40+0 records out
41943040 bytes (40.0MB) copied, 0.100185 seconds, 399.3MB/s
当超出设定容器限额内存时 直接被kill
启动一个内存限制为50M的nginx
docker run -d --name web --memory 50M --memory-swap 50M nginx
实际上docker run 加的参数 也是利用cgroup实现的 在运行一个容器后 会在cgroup生产对应的限制目录 并将tasks和Pid相关联
docker run 启动一个容器时,后台会为容器创建一个独立的控制组策略集合
docker安全加固
在对容器做内存限制时 虽然实际上只能使用50M 但是看到的总内存和容器外一样,没有做到完全的隔离,需要进行安全加固。
[root@server1 ~]# docker run -it --rm --memory 50M --memory-swap 50M busybox
/ # free -m
total used free shared buff/cache available
Mem: 990 195 507 0 288 654
Swap: 2047 0 2047
/ #
lxcfs
- 安装
yum install -y lxcfs-2.0.5-3.el7.centos.x86_64.rpm
启动lxcfs
[root@server1 ~]# lxcfs /var/lib/lxcfs/ &
[1] 4349
[root@server1 ~]# hierarchies:
0: fd: 5: cpuset
1: fd: 6: memory
2: fd: 7: blkio
3: fd: 8: pids
4: fd: 9: devices
5: fd: 10: net_prio,net_cls
6: fd: 11: freezer
7: fd: 12: perf_event
8: fd: 13: hugetlb
9: fd: 14: cpuacct,cpu
10: fd: 15: name=systemd
[root@server1 ~]# cd /var/lib/lxcfs/
[root@server1 lxcfs]# ls
cgroup proc
[root@server1 lxcfs]# cd proc/
[root@server1 proc]# ls
cpuinfo diskstats meminfo stat swaps uptime
不再使用宿主机的proc而是用/var/lib/lxcfs/proc代替
[root@server1 ~]# docker run -it --rm --memory 200M \
> -v /var/lib/lxcfs/proc/cpuinfo:/proc/cpuinfo:rw \
> -v /var/lib/lxcfs/proc/diskstats:/proc/diskstats:rw \
> -v /var/lib/lxcfs/proc/meminfo:/proc/meminfo:rw \
> -v /var/lib/lxcfs/proc/stat:/proc/stat:rw \
> -v /var/lib/lxcfs/proc/swaps:/proc/swaps:rw \
> -v /var/lib/lxcfs/proc/uptime:/proc/uptime:rw \
> ubuntu
root@24f409e6a98b:/# free -m
total used free shared buff/cache available
Mem: 200 8 191 6 0 191
Swap: 200 0 200
设置特权级运行的容器
--privileged=true
[root@server1 ~]# docker run -it --rm busyboxplus
/ # ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue 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,M-DOWN> mtu 1500 qdisc noqueue
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
/ # ip addr add 172.17.0.3/16 dev eth0
ip: RTNETLINK answers: Operation not permitted
/ # [root@server1 ~]#
[root@server1 ~]# docker run -it --rm --privileged=true busyboxplus
/ # ip addr add 172.17.0.3/16 dev eth0
/ # ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue 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
12: eth0@if13: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue
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
inet 172.17.0.3/16 scope global secondary eth0
valid_lft forever preferred_lft forever
默认情况下这个权限比较大,基本上接近宿主机的权限,为了防止用户的权限滥用, 需要增加机制,只提供给容器必要的权限,相当于做 sudo的用户权力下放
容器白名单
http://man7.org/linux/man-pages/man7/capabilities.7.html
[root@server1 ~]# docker run -it --rm --cap-add
ALL DAC_READ_SEARCH LEASE MAC_OVERRIDE SYS_ADMIN SYS_MODULE SYS_PTRACE SYS_TIME
AUDIT_CONTROL IPC_LOCK LINUX_IMMUTABLE NET_ADMIN SYS_BOOT SYS_NICE SYS_RAWIO SYS_TTY_CONFIG
BLOCK_SUSPEND IPC_OWNER MAC_ADMIN NET_BROADCAST SYSLOG SYS_PACCT SYS_RESOURCE WAKE_ALARM
[root@server1 ~]# docker run -it --rm --cap-add=NET_ADMIN busyboxplus
/ # ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue 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
14: eth0@if15: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue
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
/ # ip addr add 172.17.0.4/16 dev eth0
/ # ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue 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
14: eth0@if15: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue
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
inet 172.17.0.4/16 scope global secondary eth0
valid_lft forever preferred_lft forever
Docker安全加固思路
镜像安全
- 使用安全的基础镜像(比如看不到构建历史的不用)
- 删除镜像中的 setuid 和 setgid 权限(suid、sgid)
- 启用 docker 内容信任(证书认证);最小安装原则
- 对镜像进行安全漏洞扫描,镜像安全扫描 器(Clair)
- 容器使用非 root 运行
容器安全
- 对 docker 宿主机进行安全加固
- 限制容器之间的网络流量(某个容器流量升高,会对其他容器或主机有影响)
- 配置 docker 守护程序的 TLS 身份验证
- 启用用户命名空间支持(userns-remap )
- 限制容器内存使用量;
- 适当设置容器 CPU 优先级