前言 也是很久没有更新自己的博客了,正好前些天老师想让我搭建一个集成web题的靶场给学生测试,然后我就想用CTFd 来做靶场,有动态靶机 速度也不错。
但是当我搭建时我参考了很多网上的文章,总是会出现一些问题,运维是一件很痛苦的事情…搭建过程并不顺利。
因此当我搭建完,就想记录下搭建要记录的地方,我会结合看到的实用文章的东西来讲,也顺便让大家参考一下,少走一些弯路!故此这篇文章诞生。
(参考文章贴文末了)
以下是我搭建完的平台:(两张图片都是自己仿着buu首页手画的,马马虎虎hh)
准备工作 首先要明白搭建CTFd并不困难,但注意如果你的平台想要使用动态靶机这个功能,则需要安装CTFd-Whale插件 ,安装这个插件需要有一堆环境 要配置网络frp 使用到docker等,这些是比较麻烦的操作。
因此这里我就讲讲CTFd与CTFd-Whale插件的并同部署的过程和一些出现的问题。
注意事项
这里安装的部署环境都是用赵师傅现成的,所以配置文件按照我教程的来即可,大多不需要变化 按自己需要来!
*读者们在复制配置文件的时候,请自行将注释去掉。注释是为了方便解读,要注意的地方我也会提示大家。
保持一颗耐心、积极的心态,并且保险起见,最好每完成一个节点就打一个快照 。
基本环境安装 1.确保DNS无误 可以出网(附一个我ubuntu dns会一直刷新 修复的办法 有需要可以自取) /etc/systemd/resolved.conf DNS=8.8.8.8 114.114.114.114 #修改DNS为这个 sudo systemctl restart systemd-resolved sudo systemctl enable systemd-resolved sudo mv /etc/resolv.conf /etc/resolv.conf.bak sudo ln -s /run/systemd/resolve/resolv.conf /etc/resolv.conf
2.安装 python3 3.安装好 Docker 和 Docker-Compose Docker使用国内的daocloud一键安装命令
curl -sSL https://get.daocloud.io/docker | sh
docker-compose使用pip安装
pip install docker-compose
安装成功用-v检查一下
*4. apt换源 已经换过了可以略过这一步 echo deb http://mirrors.aliyun.com/ubuntu/ bionic main restricted universe multiverse > /etc/apt/sources.list echo deb http://mirrors.aliyun.com/ubuntu/ bionic-security main restricted universe multiverse >>/etc/apt/sources.list echo deb http://mirrors.aliyun.com/ubuntu/ bionic-updates main restricted universe multiverse >>/etc/apt/sources.list echo deb http://mirrors.aliyun.com/ubuntu/ bionic-proposed main restricted universe multiverse >>/etc/apt/sources.list echo deb http://mirrors.aliyun.com/ubuntu/ bionic-backports main restricted universe multiverse >>/etc/apt/sources.list echo deb-src http://mirrors.aliyun.com/ubuntu/ bionic main restricted universe multiverse >>/etc/apt/sources.list echo deb-src http://mirrors.aliyun.com/ubuntu/ bionic-security main restricted universe multiverse >>/etc/apt/sources.list echo deb-src http://mirrors.aliyun.com/ubuntu/ bionic-updates main restricted universe multiverse >>/etc/apt/sources.list echo deb-src http://mirrors.aliyun.com/ubuntu/ bionic-proposed main restricted universe multiverse >>/etc/apt/sources.list echo deb-src http://mirrors.aliyun.com/ubuntu/ bionic-backports main restricted universe multiverse >>/etc/apt/sources.list apt-get update
部署需要 (这里如果github下载太慢可以真机下载完后拖入虚拟机)
1.下载CTFd源码(这里是赵师傅已经换源后的源码) git clone https://github.com/glzjin/CTFd.git
注:版本是v2.3.1,修改后的版本问题比较少,但是此版本支持的主题和汉化很少,几乎没有。(所以还是比较推荐CTFd3.0以上的版本)
2.下载frp wget https://github.com/fatedier/frp/releases/download/v0.29.0/frp_0.29.0_linux_amd64.tar.gz tar -zxvf frp_0.29.0_linux_amd64.tar.gz rm -rf frp_0.29.0_linux_amd64.tar.gz
3.下载CTFd-Whale git clone https://github.com/glzjin/CTFd-Whale ctfd-whale
4.下载docker的frps git clone https://github.com/glzjin/Frp-Docker-For-CTFd-Whale frp-docker-for-ctfd-whale
确保CTFd-Whale docker-frps文件夹都是小写!
下载完后你的桌面应该是这四个文件夹。
部署过程 1.docker设置集群 这里一定要先记得设置集群,不然后面会报错。
docker swarm init docker node ls docker node update --label-add name=linux-1 <节点 ID>
如果你输入 docker swarm init
出现以下报错,则代表你先前已经开过了
使用 docker swarm leave --force
删除后,重复以上操作即可。
2.将插件ctfd-whale放入ctfd文件夹 mv ctfd-whale CTFd/CTFd/plugins/
3.为docker安装frps 注意这里frp不是装在你的服务器 你的虚拟机里的!是安装在你的docker容器里。
这里进入frp-docker-for-ctfd-whale
为docker进行安装
cd frp-docker-for-ctfd-whale docker-compose up -d
第一次安装需要等待一些时间。
看到done就是安装成功了。
可以用 docker ps
来看看docker是否运行。
可以看到这里status是up,代表正在运行。
这一步到这里就结束了,如果要修改配置文件请继续往下看,不需要则可以跳至下一步了。
docker-compose.yml
version: '2' services: frps: image: glzjin/frp:latest restart: always volumes: - ./frp:/conf/ entrypoint: - /usr/local/bin/frps - -c - /conf/frps.ini ports: - "28000-28100:28000-28100" #这里是动态靶机的开放端口段,如需修改开放端口这里要修改。 - "6490:6490" #这里如果要修改端口则必须与frps.ini配置一致 networks: default: networks: default:
frps/frps.ini
[common] bind_port = 6490 #上面的端口如果改了,这里也要跟着改 token = randomme #token不用动
*复制请注意将注释删除。
4.CTFd配置 为了和插件更好的兼容,这里要修改CTFd的docker配置文件 还要新增文件。
cd到 CTFd/
路径下,对以下文件进行修改:
1.docker-compose.yml version: '2.2' services: ctfd-nginx: image: nginx:1.17 volumes: - ./nginx/http.conf:/etc/nginx/nginx.conf #这里注意 user: root restart: always ports: - "443:443" networks: default: internal: depends_on: - ctfd cpus: '1.00' #可改 mem_limit: 150M #可改 ctfd: build: . user: root restart: always ports: - "8000:8000" #这里原本没开端口,直接打开访问网站速度会加快 environment: - UPLOAD_FOLDER=/var/uploads - DATABASE_URL=mysql+pymysql://root:ctfd@db/ctfd - REDIS_URL=redis://cache:6379 - WORKERS=1 - LOG_FOLDER=/var/log/CTFd - ACCESS_LOG=- - ERROR_LOG=- - REVERSE_PROXY=true volumes: - .data/CTFd/logs:/var/log/CTFd - .data/CTFd/uploads:/var/uploads - .:/opt/CTFd:ro - /var/run/docker.sock:/var/run/docker.sock #这里是添加的 depends_on: - db networks: default: internal: frp: ipv4_address: 172.1.0.2 cpus: '1.00' #可改 mem_limit: 450M #可改 db: image: mariadb:10.4 restart: always environment: - MYSQL_ROOT_PASSWORD=ctfd - MYSQL_USER=ctfd - MYSQL_PASSWORD=ctfd volumes: - .data/mysql:/var/lib/mysql networks: internal: # This command is required to set important mariadb defaults command: [mysqld, --character-set-server=utf8mb4, --collation-server=utf8mb4_unicode_ci, --wait_timeout=28800, --log-warnings=0] cpus: '1.00' #可改 mem_limit: 750M #可改 cache: image: redis:4 restart: always volumes: - .data/redis:/data networks: internal: cpus: '1.00' #可改 mem_limit: 450M #可改 frpc: image: glzjin/frp:latest restart: always volumes: - ./frpc:/conf/ #这里注意 entrypoint: - /usr/local/bin/frpc - -c - /conf/frpc.ini networks: frp: ipv4_address: 172.1.0.3 #记住此处 frp-containers: cpus: '1.00' #可改 mem_limit: 250M #可改 networks: default: internal: internal: true frp: driver: bridge ipam: config: - subnet: 172.1.0.0/16 frp-containers: driver: overlay internal: true ipam: config: - subnet: 172.2.0.0/16
2.nginx/http.conf 新建nginx文件夹,并创建http.conf文件 ,并将以下内容添加进去。
mkdir nginx vim nginx/http.conf
worker_processes 4; events { worker_connections 1024; } http { # Configuration containing list of application servers upstream app_servers { server ctfd:8000; } server { listen 80; client_max_body_size 4G; # Handle Server Sent Events for Notifications location /events { proxy_pass http://app_servers; proxy_set_header Connection ''; proxy_http_version 1.1; chunked_transfer_encoding off; proxy_buffering off; proxy_cache off; proxy_redirect off; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Host $server_name; } # Proxy connections to the application servers location / { proxy_pass http://app_servers; proxy_redirect off; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Host $server_name; } } }
添加完成:wq保存即可。
3.frpc/ 在CTFd/
目录下创建frpc
文件夹,并将解压的frp_0.29.0_linux_amd64
文件夹移动文件。
mkdir frpc cd ../frp_0.29.0_linux_amd64 mv frpc frpc.ini frpc_full.ini LICENSE ../CTFd/frpc
进入到frpc文件夹,对frpc.ini 进行修改:
cd ../CTFd/frpc vim frpc.ini
[common] token = randomme server_addr = 172.1.0.4 server_port = 6490 pool_count = 200 tls_enable = true admin_addr = 172.1.0.3 admin_port = 7400
这里server_port必须与frpc.ini配置一致 ,且一定要添加admin_addr和admin_port ,这里都已经满足条件了。
这里配置文件一定不能有注释,否则会导致docker运行一直restart
像我这样配置完后保存退出即可。
4.requirements.txt 官方的requeirements.txt很多版本会存在问题,所以替换成以下文件即可:
Flask==1.1.2 Werkzeug==0.16.0 Flask-SQLAlchemy==2.4.1 Flask-Caching==1.4.0 Flask-Migrate==2.5.2 Flask-Script==2.0.6 SQLAlchemy==1.3.11 SQLAlchemy-Utils==0.36.0 passlib==1.7.2 bcrypt==3.1.7 six==1.13.0 itsdangerous==1.1.0 jinja2==2.11.3 requests>=2.20.0 PyMySQL==0.9.3 gunicorn==19.9.0 normality==2.0.0 dataset==1.1.2 mistune==0.8.4 netaddr==0.7.19 redis==3.3.11 datafreeze==0.1.0 gevent==21.12.0 python-dotenv==0.10.3 flask-restplus==0.13.0 pathlib2==2.3.5 flask-marshmallow==0.10.1 marshmallow-sqlalchemy==0.17.0 boto3==1.10.39 markupsafe==1.1.1 marshmallow==2.20.2
5.配置Dokcerfile 编辑CTFd/
路径下的Dockerfile
文件,注意这里的配置文件是v2.x都适用的,到3.0之后的新版本要换一个新写法。
版本v2.x写法 FROM python:3.7-alpine RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.ustc.edu.cn/g' /etc/apk/repositories && \ apk update && \ apk add linux-headers libffi-dev gcc make musl-dev py-pip mysql-client git openssl-dev #这里注意1 RUN adduser -D -u 1001 -s /bin/bash ctfd WORKDIR /opt/CTFd RUN mkdir -p /opt/CTFd /var/log/CTFd /var/uploads COPY requirements.txt . RUN pip install -r requirements.txt -i https://mirrors.tuna.tsinghua.edu.cn/pypi/web/simple/ #这里注意2 COPY . /opt/CTFd RUN for d in CTFd/plugins/*; do \ if [ -f "$d/requirements.txt" ]; then \ pip install -r $d/requirements.txt -i https://mirrors.tuna.tsinghua.edu.cn/pypi/web/simple/ ; \ fi; \ done; #同样注意2 RUN chmod +x /opt/CTFd/docker-entrypoint.sh RUN chown -R 1001:1001 /opt/CTFd RUN chown -R 1001:1001 /var/log/CTFd /var/uploads USER 1001 EXPOSE 8000 ENTRYPOINT ["/opt/CTFd/docker-entrypoint.sh"]
*新版CTFd的写法(v2.x的可以略过了) FROM python:3.7-slim-buster WORKDIR /opt/CTFd RUN mkdir -p /opt/CTFd /var/log/CTFd /var/uploads # hadolint ignore=DL3008 RUN echo 'deb http://mirrors.aliyun.com/debian/ buster main non-free contrib \ deb http://mirrors.aliyun.com/debian/ buster-updates main non-free contrib \ deb http://mirrors.aliyun.com/debian/ buster-backports main non-free contrib \ deb-src http://mirrors.aliyun.com/debian/ buster main non-free contrib \ deb-src http://mirrors.aliyun.com/debian/ buster-updates main non-free contrib \ deb-src http://mirrors.aliyun.com/debian/ buster-backports main non-free contrib \ deb http://mirrors.aliyun.com/debian-security/ buster/updates main non-free contrib \ deb-src http://mirrors.aliyun.com/debian-security/ buster/updates main non-free contrib'> /etc/apt/sources.list && \ apt-get update \ && apt-get install -y --no-install-recommends \ build-essential \ default-mysql-client \ python3-dev \ libffi-dev \ libssl-dev \ git \ && apt-get clean \ && rm -rf /var/lib/apt/lists/* COPY requirements.txt /opt/CTFd/ RUN pip install -r requirements.txt -i http://mirrors.aliyun.com/pypi/simple/ --no-cache-dir COPY . /opt/CTFd # hadolint ignore=SC2086 RUN for d in CTFd/plugins/*; do \ if [ -f "$d/requirements.txt" ]; then \ pip install -r $d/requirements.txt -i http://mirrors.aliyun.com/pypi/simple/ --no-cache-dir; \ fi; \ done; RUN adduser \ --disabled-login \ -u 1001 \ --gecos "" \ --shell /bin/bash \ ctfd RUN chmod +x /opt/CTFd/docker-entrypoint.sh \ && chown -R 1001:1001 /opt/CTFd /var/log/CTFd /var/uploads USER 1001 EXPOSE 8000 ENTRYPOINT ["/opt/CTFd/docker-entrypoint.sh"]
编辑完退出即可。
6.开启部署 准备工作已就绪,剩下的直接交给docker去build构建即可
一定要切换到 CTFd
路径下 ,执行:(这一句话等同于docker-compose build + docker-compose up -d)
docker-compose up -d --build
(有安装卡住的部分ctrl+c后重新运行下就好)
如图所示,有WARNING即为在集群网络类,是正常情况
这边显示done就是开启成功了,等待安装完成后,查看容器是否运行:
可以看到这里平台已经映射到了真机的8000端口了
访问 http://ip:8000/ 即可访问搭建好的CTFd平台了。
7.配置CTFd 1.初始化 刚搭好平台,要初始化设置一下,这里跟着我设置即可。
设置错了也没关系,后续在后台也能修改。
2.配置CTFd-Whale 设置好后进入后台 Admin Panel
找到 Plugins -> ctfd-whale
属性
配置
Docker API URL
unix://var/run/docker.sock
Frp API IP
frpc的ip配置
Frp API Port
frpc的端口配置
Frp Http Domain Suffix
Docker API URL to connect(可填None)
Frp Http Port
80
Frp Direct IP Address
你的公网ip
Frp Direct Minimum Port
与之前frps最小端口呼应
Frp Direct Minimum Port
与之前frps最大端口呼应
Max Container Count
不超过最大-最小
Max Renewal Times
最大实例延时次数
Frp config template
填入frps的配置,只需填[common]
Docker Auto Connect Containers
ctfd_frpc_1
Docker Dns Setting
可填机器内DNS,没有可填个外网DNS
Docker Swarm Nodes
linux-1 与前面swarm集群呼应
Docker Multi-Container Network Subnet
内网题大子网ip配置/CIDR
Docker Multi-Container Network Subnet New Prefix
每个内网题实例的CIDR
Docker Container Timeout
单位为秒
按照图片的来配置即可
配置好了后记得点击 update 更新
8.设置docker网络 此时运行 docker ps
能发现frpc在一直重启
这是因为我们还没有配置网络
运行 docker network ls
显示ctfd_frp-containers
运行 docker network inspect ctfd_frp
查看网络情况
能发现这里只有ctfd,没有接入frpc和frps,先用docker ps
分别找到frps和frpc的ID
我这里两个是 frpc:6a7a98e8cd38
frps:5192ccf2c8cb
然后使用docker network connect ctfd_frp <frp容器id>
连接
docker network connect ctfd_frp 6a7a98e8cd38 docker network connect ctfd_frp 5192ccf2c8cb
此时再查看网络情况docker network inspect ctfd_frp
能发现多了frpc和frps
重启docker网络:
docker restart ctfd_frpc_1 frp-docker-for-ctfd-whale_frps_1
最后docker ps验证下,能发现frpc可以一直运行了。
修复成功。
9.部署完毕,新建题目测试 如果做到这一步 过程都成功了,那么恭喜你已经成功搭建了CTFd和CTFd-Whale插件了,现在进行题目的测试。
后台创建题目:
然后贴下赵总的图:
这里直接用 ctftraining/qwb_2019_supersqli
dockerhub的镜像来测试
创建成功后 再添加flag和题目显示情况即可 (这些选项在后台的题目详情也可以添加、修改)
完成后就在 Challenges
点击题目测试
开启靶机,访问靶机端口(第一次开启可能会等的比较久):
可以看到成功开启来了,docker ps也能看到docker运行状况:
一键部署脚本 这个脚本是我自己写的,为了方便一键开启CTFd,适用于访问ip不到网站的情况下,使用python3运行大概率可以开启服务。
临时写的,比较糙 不过效果还可以 大多都是能开起来的。
使用方法 将脚本放入你的CTFd
文件夹,使用python3运行即可
start.py import osimport redef shutdown (): cmd = 'docker-compose down' os.system(cmd) def start (): cmd = "docker-compose up -d" os.system(cmd) def check (): cmd = 'docker ps' r = '' for i in os.popen(cmd): r += i return r def network (): c1 = "docker network inspect ctfd_frp" r = '' for i in os.popen(c1): r += i return r def connect (id ): c1 = f"docker network connect ctfd_frp {id } " os.system(c1) def restart (): c1 = 'docker restart ctfd_frpc_1 frp-docker-for-ctfd-whale_frps_1' os.system(c1) os.system('docker network disconnect -f ctfd_frp frp-docker-for-ctfd-whale_frps_1' ) print (f'[*] Restarting!' )shutdown() start() content = check() ctfdid = re.findall('\n(.*?) ctfd_ctfd' ,content)[-1 ] print (f'[*] Now checking network!' )cmd = f'docker exec -it {ctfdid} python -c \'import requests;r = requests.get("http://172.1.0.3:7400/api/reload");print(r)\'' r = '' for i in os.popen(cmd): r += i if 'Connection refused' in r: print (f'[*] network fixing' ) c2 = network() if 'frp-docker-for-ctfd-whale_frps_1' not in c2: content = check() frpsid = re.findall('(.*?) frp-docker-for-ctfd-whale_frps_1' ,content)[0 ].split('glzjin/frp:latest' )[0 ].strip() connect(frpsid) print (f'[+] frps append in ctfd success!' ) restart() f = open ('frpc/frpc.ini' ,'w' ) main="""[common] token = randomme server_addr = 172.1.0.4 server_port = 6490 pool_count = 200 tls_enable = true admin_addr = 172.1.0.3 admin_port = 7400 """ f.write(main) f.close() print (f'[+] Config(frpc.ini) overwriting success!' ) os.system('docker restart ctfd_frpc_1' ) r = '' for i in os.popen(cmd): r += i if '<Response [200]>' in r: print ('[+] Start Success!' )
一些值得注意的地方 帮助调试的命令
docker logs <ID> #查看一个docker容器的运行日志
*如果容器一直处于restart的状态,使用这条命令查看其运行情况,再根据报错去网上查询,非常有效且高效!
docker exec -it <ID> sh #进入一个容器,查看内部情况
docker network prune -f #删除未使用的网络,适用于重启服务某些网络未关闭的情况
疑难杂症 如果docker容器无法启动或者frp端口无法映射可以进容器检查
确保docker api填写正确,如docker-compose.yml中写的unix:///var/run/docker.sock 你也可以使用端口形式的api如官方示例 :可以用IP:端口指定API
docker容器无法启动问题 进入容器检查:
docker exec -it <ctfd容器id> sh /opt/CTFd# python >>>import docker >>>client=docker.DockerClient(base_url="unix:///var/run/docker.sock") >>>client.images.list()
如果api正确会列出所有镜像
frp端口无法映射问题 进入容器检查:
//其实检查可以顺便检查一下上面的,因为都在ctfd容器内 docker exec -it <ctfd容器id> sh /opt/CTFd# python >>>import requests >>>requests.get("http://172.1.0.3:7400/api/reload")//即frp api的地址 返回 <Response [200]> #表示成功
如果frpc还是出现如下问题
requests.exceptions.ConnectionError: HTTPConnectionPool(host='172.1.0.3', port=7400): Max retries exceeded with url: /api/reload (Caused by NewConnectionError('<urllib3.connection.HTTPConnection object at 0x7f8df919f850>: Failed to establish a new connection: [Errno 111] Connection refused'))
则重新配置frpc。编辑CTFd/frpc/frpc.ini
[common] token = randomme server_addr = 172.1.0.4 server_port = 6490 pool_count = 200 tls_enable = true admin_addr = 172.1.0.3 admin_port = 7400
然后运行docker restart ctfd_frpc_1
(这里再看frpc.ini会发现内容更新了,admin配置没了,不用担心)
再进容器检查requests.get("http://172.1.0.3:7400/api/reload")
应该就可以了<Response [200]>
frp端口冲突 如果发现frp端口冲突,请检查本地frpc或者frps服务,frp是在docker上不是在本地 的!
systemctl stop frpc systemctl stop frps
部署成功 如果到这一步都没有出现问题,非常恭喜你,CTFd和动态靶机部署已经成功搭建。
这篇文章就到此结束了,下一篇文章是关于平台的后续工作:平台的美化、优化、题目镜像制作等。
感谢你的观看,希望能帮到你!也可以在这篇文章评论进行讨论问题
参考文章 这里我贴出几个对我有帮助的文章,非常详细!我的这篇文章都是结合各位师傅的教程和经验总结出来的。
https://err0r.top/article/CTFD/ https://www.zhaoj.in/read-6333.html(赵师傅官方文章) https://www.yuque.com/dat0u/ctfd