# 我们运行一个容器
[root@server1 ~]# docker run -it --rm --memory 256MB --memory-swap 256MB ubuntu
root@3f308cdd0b55:/# free -m # 查看内存
total used free shared buffers cached
Mem: 991 433 557 12 2 249
-/+ buffers/cache: 181 809
Swap: 2047 0 2047
root@3f308cdd0b55:/# exit
[root@server1 ~]# free -m # 容器外查看内存
total used free shared buff/cache available
Mem: 991 125 579 12 286 704
Swap: 2047
- 发现我们开启容器时虽然已经限制了内存为256M,但是看到的总内存和容器外一样,没有做到完全的隔离,这是一个不安全的点,需要进行安全加固。
lxcfs 文件系统
首先我们可以通过 lxcfs 文件系统增强 docker 容器的隔离性和资源可见性.
[root@server1 ~]# docker run -it --name vm1 -m 256M ubuntu # 运行一个容器
root@6718c6a02544:/# free -m
total used free shared buffers cached
Mem: 991 434 556 12 2 251
-/+ buffers/cache: 180 810
Swap: 2047 0 2047
# 内存大小和外界相同。
root@6718c6a02544:/# cat /proc/cpuinfo
processor : 0
vendor_id : GenuineIntel
cpu family : 6
model : 61
model name : Intel Core Processor (Broadwell)
stepping : 2
microcode : 0x1
cpu MHz : 2495.998
cache size : 4096 KB
physical id : 0
siblings : 1
core id : 0
cpu cores : 1
apicid : 0
initial apicid : 0
fpu : yes
fpu_exception : yes
# 这里和我们在外面看到的时一样的,因为它的信息都是共享宿主机的。
我们安装 lxcfs 文件系统:
[root@server1 lxcfs]# yum install lxcfs-2.0.5-3.el7.centos.x86_64.rpm -y
[root@server1 ~]# lxcfs /var/lib/lxcfs/ & # 后台运行启动,/var/lib/lxcfs 是 lxcfs 默认数据目录
[1] 3876
[root@server1 ~]# hierarchies:
0: fd: 5: freezer
1: fd: 6: devices
2: fd: 7: memory
3: fd: 8: cpuacct,cpu
4: fd: 9: hugetlb
5: fd: 10: pids
6: fd: 11: net_prio,net_cls
7: fd: 12: blkio
8: fd: 13: cpuset
9: fd: 14: perf_event
10: fd: 15: name=systemd
[root@server1 ~]# cd /var/lib/lxcfs/
[root@server1 lxcfs]# ls
cgroup proc # ccgroup 是资源限制,proc 是系统信息
[root@server1 proc]# ls
cpuinfo diskstats meminfo stat swaps uptime
# 所以我们可以把这个目录挂载到容器里面,就可以看到我们想要的信息了,我们只需要在这里限制就行了
设置特权级运行的容器:
加 --privileged=true 参数。
有时候我们需要容器具备更多权限,比如操作内核模块,控制 swap 分区,修改 MAC 地址等。
root@9b2f8448999b:/# id # 当前是root用户
uid=0(root) gid=0(root) groups=0(root)
root@9b2f8448999b:/# ip addr add 172.17.0.3/24 dev eth0
RTNETLINK answers: Operation not permitted # 加一个ip地址却不被允许,因为没有实际权限
[root@server1 ~]# docker run --help | grep priv # 查看权限参数
--privileged Give extended privileges to this container # 给额外的权限
#默认这个权限是被禁掉的
[root@server1 proc]# docker run -it --name vm3 --privileged=true ubuntu # 运行成功
root@c685225f26f0:/# ip a
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
12: eth0@if13: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
link/ether 02:42:ac:11:00:03 brd ff:ff:ff:ff:ff:ff
inet 172.17.0.3/16 brd 172.17.255.255 scope global eth0
valid_lft forever preferred_lft forever
root@c685225f26f0:/# ip addr add 172.17.0.10 dev eth0 # 添加一个ip地址
root@c685225f26f0:/# ip a
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
12: eth0@if13: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
link/ether 02:42:ac:11:00:03 brd ff:ff:ff:ff:ff:ff
inet 172.17.0.3/16 brd 172.17.255.255 scope global eth0
valid_lft forever preferred_lft forever
inet 172.17.0.10/32 scope global eth0 # 添加成功。
valid_lft forever preferred_lft forever
[root@server1 proc]# docker inspect vm3 | grep Pri
"Privileged": true, # 当前只有vm3 开启了这个权限
[root@server1 proc]# docker inspect vm2 | grep Pri
"Privileged": false,
[root@server1 proc]# docker inspect vm1 | grep Pri
"Privileged": false,
- 默认情况下这个权限比较大,基本上接近宿主机的权限,为了防止用户的权限滥用, 需要增加机制,只提供给容器必要的权限’ 相当于做 sudo的用户权力下放
我们通过设置容器白名单实现。
容器白名单
http://man7.org/linux/man-pages/man7/capabilities.7.html
可以在这个网址进行capability 权限查询
[root@server1 proc]# docker run -it --name vm4 --cap-add=NET_ADMIN ubuntu # 这个参数代表只可以控制网络,但其他不行
root@c8a9b1998cfb:/# ip addr add 172.17.0.5/24 dev eth0
root@c8a9b1998cfb:/# ip a
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
14: eth0@if15: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
link/ether 02:42:ac:11:00:04 brd ff:ff:ff:ff:ff:ff
inet 172.17.0.4/16 brd 172.17.255.255 scope global eth0
valid_lft forever preferred_lft forever
inet 172.17.0.5/24 scope global eth0
valid_lft forever preferred_lft forever
root@c8a9b1998cfb:/#
[root@server1 proc]# docker inspect vm4 |grep Pri
"Privileged": false, # 但是此时的 privileged 是关闭的
[root@server1 proc]# docker inspect vm4 | less
"CapAdd": [
"NET_ADMIN" # 可以看到我们加的参数
],
# 我们还可以用这种方式查看:
[root@server1 proc]# docker inspect -f {{.HostConfig.Privileged}} vm4
false
[root@server1 proc]# docker inspect -f {{.HostConfig.CapAdd}} vm4
[NET_ADMIN]
Docker安全加固思路
-
保证镜像安全:
使用安全的基础镜像(比如看不到构建历史的不用);删除镜像中的 setuid 和 setgid 权限(suid、sgid);;启用 docker 内容信任(证书认证);最小安装原则;对镜像进行安全漏洞扫描,镜像安全扫描 器(Clair);容器使用非 root 运行 -
保证容器安全:
对 docker 主机进行安全加固;限制容器之间的网络流量(某个容器流量升高,会对其他容器或主机有影响);配置 docker 守护程序的 TLS 身份验证;启用用户命名空间支持(userns-remap 参数,在容器第一次启动前就要加上,写在 /etc/docker/daemon.json 文件里,还需要对 docker 数据目录做权限配置 /var/lib/docker);限制容器内存使用量;适当设置容器 CPU 优先级,/sys/fs/cgroup/cpu/cpu.shares 这个文件里 -
docker 安全遗留问题:
默认情况下只有 6 个 namespace,很多主要的内核子系统是没有命名空间的,如 SELinux,Cgroup,
/sys 下的文件系统,/proc/sys,/proc/sysrq-trigger 等
[root@server1 ns]# ls /sys
block bus class dev devices firmware fs hypervisor kernel module power
[root@server1 ns]# ls /proc/sys
abi crypto debug dev fs kernel net sunrpc user vm
[root@server1 ns]# ls /proc/sysrq-trigger
/proc/sysrq-trigger
[root@server1 ~]# docker inspect vm4| grep Pid
"Pid": 4204, # 进程为4024
"PidMode": "",
"PidsLimit": 0,
[root@server1 ~]# cd /proc/4204/ns
[root@server1 ns]# ls
ipc mnt net pid user uts # 可以看到只有六个命名空间
命名空间 | 功能 | 系统调用参数 | 内核版本 |
---|---|---|---|
MNT Namespace(mount) | 提供磁盘挂载点和文件系统的隔离能力 | CLONE_NEWNS | Linux 2.4.19 |
IPC Namespace(Inter-Process Communication) | 提供进程间通信的隔离能力 | CLONE_NEWIPC | Linux 2.6.19 |
UTS Namespace(UNIX Timesharing System) | 提供主机名隔离能力 | CLONE_NEWUTS | Linux 2.6.19 |
PID Namespace(Process Identification) | 提供进程隔离能力 | CLONE_NEWPID | Linux 2.6.24 |
Net Namespace(network) | 提供网络隔离能力 | CLONE_NEWNET | Linux 2.6.29 |
User Namespace(user) | 提供用户隔离能力 | CLONE_NEWUSER | Linux 3.8 |
比如我们的设备就没有命名空间:/dev/mem(进程和物理地址的映射),/dev/sd*文件系统设备,内核模
块等,都还需要进一步的改进。