容器的安全性问题的根源在于容器和宿主机共享内核。如果容器里的应用导致Linux内核崩溃,那么整个系统可能都会崩溃。与虚拟机是不同的,虚拟机并没有与主机共享内核,虚拟机崩溃一般不会导致宿主机崩溃。
虚拟机通过添加 Hypervisor 层(虚拟化中间层),虚拟出网卡、内存、CPU 等虚拟硬件,再在其上建立虚拟机,每个虚拟机都有自己的系统内核。 而Docker容器则是通过隔离的方式,将文件系统、进程、设备、网络等资源进行隔离,再对权限、CPU 资源等进行控制,最终让容器之间互不影响,容器无法影响宿主机。 容器与宿主机共享内核、文件系统、硬件等资源。
与虚拟机相比,容器资源损耗要少。同样的宿主机下,能够建立容器的数量要比虚拟机多。 但是,虚拟机的安全性要比容器稍好,要从虚拟机攻破到宿主机或其他虚拟机,需要先攻破 Hypervisor 层,这是极其困难的。 而 docker 容器与宿主机共享内核、文件系统等资源,更有可能对其他容器、宿主机产生影响。
特性 | Docker容器 | 虚拟机 |
---|---|---|
启动速度 | 秒级 | 分钟级 |
计算能力损耗 | 相当于宿主机的进程,几乎无损耗 | 需要通过虚拟机管理程序对宿主机资源虚拟访问,损耗 20-50%左右 |
性能 | 接近原生 | 弱于物理机 |
系统支持单机容量 | 上千个容器 | 几十个虚拟机 |
隔离性 | 通过namespace实现资源隔离,通过cgroup实现限制资源的最大使用量 | 完全隔离。每个虚拟机都有独立的硬件资源 |
内核 | 所有容器共享宿主机的内核 | 每个虚拟机都有独立的操作系统和内核 |
作为一款应用 Docker 本身实现上会有代码缺陷。CVE 官方记录 Docker 历史版本共有超过 20 项漏洞,可参见 Docker 官方网站。 黑客常用的攻击手段主要有代码执行、权限提升、信息泄露、权限绕过等。目前 Docker 版本更迭非常快,Docker 用户可将 Docker 升级为最新版本。
Docker 提供了 Docker hub,可以让用户上传创建的镜像,以便其他用户下载,快速搭建环境。但同时也带来了一些安全问题。 例如下面三种方式:
(1)黑客上传恶意镜像
(2)镜像使用有漏洞的软件
(3)中间人攻击篡改镜像
1)尽量不用--privileged运行容器(授权容器root用户拥有宿主机的root权限)
2)尽量不用--network host运行容器(使用host网络模式共享宿主机的网络命名空间)
3)尽量不在容器中运行ssh服务
4)尽量不把宿主机系统的关键敏感目录挂载到容器中
1)尽量使用较小体积的镜像
2)尽量以单一应用程序运行容器
3)尽量在容器中使用最新稳定版本的应用
4)尽量安装使用最新稳定版本的docker
5)尽量以最低权限运行容器
6)尽量使用官方提供的镜像或自己构建的镜像
7)尽量给docker目录(默认/var/lib/docker)分配独立的文件系统
8)尽量以资源限制的方式运行容器 -m --memory-swap --cpu-quota --cpu-shares --cpuset-cpus --device-write-bps
9)尽量以只读的方式挂载数据卷,持久化容器数据到宿主机的除外 -v 宿主机目录:容器数据卷目录:ro
10)尽量设置容器的重启策略和次数 --restart always|on-failure:N
11)尽量对镜像进行漏洞扫描
---------------------------------------------------------------------------------------------------------------------
使用最少资源和最低权限运行容器,此为dokcer容器安全的核心思想
---------------------------------------------------------------------------------------------------------------------
http | https | |
---|---|---|
协议 | 超文本传输协议 | 安全超文本传输协议 |
端口 | tcp/80 | tcp/443 |
传输方式 | 明文传输 | 密文传输 |
加密方式 | 证书加密(ssl/tls加密)的方式 |
0)服务端会事先通过CA签发服务端证书和私钥文件
1)客户端发送https请求到服务端的443端口
2)服务端会返回一个包含公钥、证书有效期、CA机构等信息的证书给客户端
3)客户端收到服务端发来的证书后,会先用本地的CA证书验证服务端证书的有效性,如果证书有效,则会再客户端本地生成一个随机会话密钥并通过证书中的公钥进行加密,再发送给服务端
4)服务端会用私钥进行解密获取客户端发来的随机会话密钥,并使用这个会话密钥加密要返回的报文,再发送给客户端
5)客户端也是这个会话密钥获取服务端返回的数据,之后双方可继 续这个会话密钥进行加密和解密来实现密文通信
0)服务端和客户端会事先通过CA签发服务端证书和私钥文件
1)客户端发送https请求到服务端的443端口
2)服务端会先返回一个包含公钥、证书有效期、CA机构等信息的证书给客户端
3)客户端收到服务端发来的证书后,会先用本地的CA证书校验服务端证书的有效性,如果证书有效,再将包含公钥的客户端证书发送给服务端
4)服务器收到客户端证书后也会校验证书的有效性,如果校验通过,客户端发送自己可支持的加密方案给服务器
5)服务端会选择加密成都高的方案,并通过客户端证书里的公钥加密后发送给客户端
6)客户端使用私钥解密获取选择的加密方案,并使用次加密方案生成随机会话密钥,再通过服务端证书中的公钥进行加密,再发送给服务端。
7)服务端会用私钥进行解密获取客户端发来的随机会话密钥,并使用这个会话密钥加密要返回的保温,再发送给客户端
8)客户端也是这个绘画密钥进行解密获取服务端返回的数据,之后双方可继续这个会话密钥进行加密和解密来实现密文通信
1)在阿里云、华为云、腾讯云、等云服务商哪里申请一年有效期的免费证书或者购买证书
2)在本地使用openssl、mkcert、cfssl、certbot(Let's Encrypt)的工具生成本地私钥证书
1)编译安装时需要添加 --with-http_ssl_module 模块
2)修改配置文件,添加 ssl 配置
http {
server {
listen 443 ssl; #SSL访问端口号为 443
server_name 域名; #指定绑定证书的域名
ssl_certificate /usr/local/nginx/conf/cert/xxxxx.pem; #指定服务端证书文件路径
ssl_certificate_key /usr/local/nginx/conf/cert/xxxxx.key; #指定服务端私钥文件路径
# ssl_session_cache shared:SSL:1m;
ssl_session_timeout 5m;
ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2; #指定SSL服务器端支持的协议版本
ssl_prefer_server_ciphers on;
location / {
root html;
index index.html index.htm;
}
}
}