dockerfile编写学习

0x 01 前言

​ 镜像的定制实际上就是定制每一层所添加的配置文件,如果我们可以把每一层的修改、安装、构建、操作的命令都写入一个脚本,然后用这个脚本来构建、定制镜像,那么镜像构建透明性的问题、体积的问题就会得到解决,这个脚本就是 Dockerfile; Dockerfile 是一个文本文件,其内包含了一条条的指令,每一条指令构建一层,每一层指令的内容,就是描述该层应该如何构建,然后通过 commit 构成新的镜像。

0x 02 Dockerfile 常用参数

FROM

FROM:指定基础镜像,必须是第一条指令,格式为FROM imageFROM image:tag

1
2
3
4
# 定制 nginx 镜像的 Dockerfile

FROM nginx
RUN echo '<h1>Hello,Docker!</h1>' > /usr/share/nginx/html/index.html

注: Docker Hub 上有很多高质量的服务类的官方镜像可以拿来直接使用,比如:nginx 、redis 、mysql 、php 、mongo \ tomcat 等,可以在其中找最符合的一个进行定制

RUN

RUN :用来执行命令行命令,格式有两种:

  1. shell 格式: RUN <命令>,就像直接在命令行中输入的命令一样

  2. exec 格式:RUN ["可执行文件",“参数1”,“参数2”],更像是函数调用中的格式

warning:每一个RUN命令都会在 docker镜像中新建一层,所以应该尽量少用 RUN 命令,而且要在RUN 的最后要做必要的清除工作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 一层构建,并在最后清理压缩包等缓存文件
FROM debian:stretch

RUN buildDeps='gcc libc6-dev make wget' \
&& apt-get update \
&& apt-get install -y $buildDeps \
&& wget -O redis.tar.gz "http://download.redis.io/releases/redis-5.0.3.tar.gz" \
&& mkdir -p /usr/src/redis \
&& tar -xzf redis.tar.gz -C /usr/src/redis --strip-components=1 \
&& make -C /usr/src/redis \
&& make -C /usr/src/redis install \
&& rm -rf /var/lib/apt/lists/* \
&& rm redis.tar.gz \
&& rm -r /usr/src/redis \
&& apt-get purge -y --auto-remove $buildDeps

COPY

COPY : 用来从构建上下文目录中<原路径>的文件/目录复制到新一层镜像内的 <目标路径>位置,格式有两种:

1,shell 格式:COPY [--chown=<user>:<group>] <原路径>...<目标路径>

2,exec 合适:COPY[--chown=<user>:<group>] ["原路径1",... "<目标路径>"]

原路径:可以是多个,甚至可以是通配符

目标路径:可以是容器内的绝对路径,也可以是相对于工作目录的相对路径(工作目录可以用 WORKDIR 指令来指定,不需要事先创建,会自动创建)

1
2
3
# 利用 通配符 进行复制
COPY hom* /mydir/
COPY hom?.txt /mydir/

note : COPY 会将原文件的各种数据都保留,比如 读、写、执行权限,可以通过 –chown=: 选项来改变文件的所属用户及所属组。

CMD

1
2
3
4
5
CMD ["executable", "param1", "param2"], 推荐使用该方式

CMD ["param1", "param2"],为ENTRYPOINT指令提供预设参数

CMD command param1 param2 在SHELL中执行

CMD指令的主要目的是为执行容器提供默认值,每个Dockerfile只有一个CMD命令,如果指定了多个CMD命令,也只会有一条执行,如果启动容器的时候指定了运行的命令,则会覆盖掉CMD指定的命令。

EXPOSE

语法:EXPOSE <端口1> [<端口2>...]

  该条指令是声明运行时容器提供的服务端口,这只是一个声明,在运行时并不会因为这个声明应用就会开启这个端口的服务。这样声明带来两个好处:

  1,帮助镜像使用者理解这个镜像服务的守护端口,以方便配置映射

  2,在运行时使用随机端口映射,也就是 docker run -P 时,会自动随机映射 EXPOSE 的端口

note: 要将 EXPOSE 和在运行时使用 -p <宿主端口>:<容器端口> 区分开来。-p 是映射宿主端口和容器端口,就是将容器的对应端口服务公开给外界访问,而 EXPOSE 仅仅是声明容器打算使用什么端口而已,并不会在宿主进行端口映射。

WORKDIR

格式为:WORKDIR /path/to/workdir

切换⽬录指令,类似于cd命令,对RUN、CMD、ENTRYPOINT⽣效。

1
WORKDIR /var/www/html

LABEL

使用LABEL指令,可以为镜像设置元数据,例如镜像创建者或者镜像说明。旧版的 Dockerfile 语法使用MAINTAINER指令指定镜像创建者,但是它已经被弃用了。有时,一些外部程序需要用到镜像的元数据,例如nvidia-docker需要用到com.nvidia.volumes.needed。示例如下:

1
2
3
4
FROM node:7-alpine
LABEL maintainer "jakub.skalecki@example.com"
或者
LABEL Author="hwlanxiaojun" Blog="https://hwlanxiaojun.github.io"

ENV

ENV : 用来设置环境变量,格式有两种:

  1,ENV

  2,ENV = =

在设置了环境变量之后,无论是后面的其它指令,如 RUN ,还是运行时的应用,都可以直接使用这里定义的环境变量

1
2
3
4
5
6
# 定义环境变量
ENV VERSION=1.0 DEBUG=ON \
NAME="Happy Feet"

# 官方 node 镜像 Dockerfile 中:
ENV NODE_VERSION 7.2.0

0x 03 构建镜像

1
2
3
docker build -t webprac .
//其中.表示当前路径,构建的时候要保证jar包和Dockerfile文件在同一个文件夹下,-t: 镜像的名字及标签,webprac是标签名
ocker run -dit -p 8080:80 webprac

0x 04 动态flag编写

动态flag的存在形式一般有以下三种:文件中、执行readflag、数据库中

文件

linux下,sed命令可以将file中每行的字符串进行替换:

1
sed -i 's/原字符串/新字符串/' file

export命令可以查看、设置环境变量

那么在docker-php-entrypoint文件中写入:

1
2
3
4
5
#!/bin/sh

sed -i "s/flag_here/$FLAG/" flag.php
export FLAG=not_flag
FLAG=not_flag

就会根据当前环境变量的FLAG值来替换掉flag.php中的flag_here,因此还需要在flag.php中设置:

1
2
<?php
$flag = flag_here

这样,在Dockerfile目录执行docker build -t test .将Dockerfile文件打包为镜像test,使用安装了动态靶机插件的CTFd平台部署后,该插件在启动靶机时会自动生成flag并应用到环境变量中,这样便实现了生成动态flag。

readflag

先来看下写法:

1
2
3
4
5
6
7
8
#!/usr/bin/env bash

echo $FLAG > /flag
chmod u+s /readflag
chmod 400 /flag

export FLAG=not_flag
FLAG=not_flag

首先将环境变量$FLAG写入到/flag中,之后进行权限配置:

1.chmod u+s /readflag : 为/readflag文件加上setuid标志,设置使文件在执行阶段具有文件所有者的权限

2.chmod 400 /flag :将flag文件权限设为文件拥有者只读,群组和其他用户没有任何权限

那么执行./readflag时,就会拥有文件所有者的权限,可以读取/flag

数据库

flag.sh:

1
2
3
4
5
6
7
8
9
#!/bin/bash

# 修改数据库中的 FLAG
mysql -e "CREATE DATABASE IF NOT EXISTS supersqli;USE supersqli; CREATE TABLE IF NOT EXISTS \`1919810931114514\` (\`flag\` varchar(100) NOT NULL) ENGINE=MyISAM DEFAULT CHARSET=utf8;INSERT INTO \`1919810931114514\` VALUES ('$FLAG');" -uroot -proot

export FLAG=not_flag
FLAG=not_flag

rm -f /flag.sh

0x 05 打包镜像到Docker Hub

为了方便搜索和下载自己编写的镜像,可以通过以下步骤来实现:

1.到https://hub.docker.com/注册一个账号

2.登录:

1
docker login -u 用户名 -p 密码

3.更改镜像标签,格式为用户名/标签名

1
docker tag test 用户名/镜像名

4.推送:

1
docker push 用户名/镜像名

5.登出:

1
docker logout

这时就可以在Docker Hub上找到刚上传的镜像了,将其添加描述后就能使用docker search搜索到自己的镜像

0x 06 ctfd平台部署

参考这几篇文章:

https://www.zhaoj.in/read-6333.html

https://blog.csdn.net/fjh1997/article/details/100850756

https://mp.weixin.qq.com/s?__biz=MzA3NjE0Mzk2OA==&mid=2648922692&idx=1&sn=6f27333cf2f12008d62bfd94bc1d2f6a&chksm=8772364bb005bf5d8d377a575fe4730f35235ff43ab597f8e5f17603da4801fab74e402a8916&mpshare=1&scene=23&srcid=&sharer_sharetime=1570805873725&sharer_shareid=7fa312c2aff8bd738bf9d331ff765678#rd

0x 07 docker-compose

docker-compose.yml:告诉docker-compose该怎么编排一组服务

参考链接:

https://www.cnblogs.com/zpcoding/p/11450686.html

https://www.cnblogs.com/fundebug/p/write-excellent-dockerfile.html

http://www.gtfly.top/2020/03/12/2019-09-27-CTFd%E5%8A%A8%E6%80%81docker%E9%95%9C%E5%83%8F%E7%BC%96%E5%86%99/