1346 字
7 分钟
CTF院赛平台搭建笔记

这次我们学院要举办CTF比赛,按照老师的意思是要随便搞一下,但是喜欢折腾的我肯定不同意了(虽然我不是信安组的但就是喜欢玩这些东西,刚好早期有部署过GZCTF,我直接跟信安的同学说你们的平台我来处理。

这次部署平台的方案:一台内网CTF主机+一台公网云主机并结合easytier构建虚拟局域网并用caddy自动SSL。

系统版本如下: 公网云主机:Debian GNU/Linux 11 内网CTF主机:openEuler release 25.03

基础设置(云主机 和 内网CTF主机)#

Terminal window
$ systemctl stop firewalld && systemctl disable firewalld
$ setenforce 0

部署easytier(云主机 和 内网CTF主机)#

下载easytier#

Terminal window
$ wget https://ghfast.top/https://github.com/EasyTier/EasyTier/releases/download/v2.2.4/easytier-linux-x86_64-v2.2.4.zip

解压压缩包#

Terminal window
$ unzip easytier-linux-x86_64-v2.2.4.zip
# 如果没有unzip下载
# dnf install -y unzip
# apt install -y unzip

移动目录#

Terminal window
$ mv easytier-linux-x86_64 /usr/bin/easytier

编写service服务#

--ipv4 10.144.144.1手动分配虚拟局域网的IP地址

--network-name XXSS虚拟局域网络名称

--network-secret XXSS001虚拟局域网的密码

PS:这里我只展示其中一台的配置文件,其实要改的就是IP地址,因为我要自己分配IP地址所以我就在配置文件里指定IP

Terminal window
$ cat >> /etc/systemd/system/easytier.service << EOF
[Unit]
Description=EasyTier Service
After=network.target syslog.target
Wants=network.target
[Service]
Type=simple
ExecStart=/usr/bin/easytier/easytier-core --ipv4 10.144.144.1 --network-name XXSS --network-secret XXSS001
[Install]
WantedBy=multi-user.target
EOF

启动easytier服务#

Terminal window
$ systemctl daemon-reload
$ systemctl enable easytier
$ systemctl start easytier
$ systemctl status easytier
easytier.service - EasyTier Service
Loaded: loaded (/etc/systemd/system/easytier.service; disabled; preset: disabled)
Active: active (running) since Fri 2025-05-16 13:44:41 CST; 7h ago
Main PID: 269640 (easytier-core)
Tasks: 4 (limit: 98868)
Memory: 46.0M ()
CGroup: /system.slice/easytier.service
└─269640 /usr/bin/easytier/easytier-core --ipv4 10.144.144.2 --network-name XXSS --network>
......

部署GZCTF平台#

配置Docker环境#

Terminal window
[root@CTFGZ ~]# dnf install -y docker-client docker-engine

配置docker-compose#

Terminal window
[root@CTFGZ ~]# wget https://ghfast.top/https://github.com/docker/compose/releases/download/v2.36.0/docker-compose-linux-x86_64
[root@CTFGZ ~]# mv docker-compose-linux-x86_64 /usr/bin/docker-compose
[root@CTFGZ ~]# chmod +x /usr/bin/docker-compose

启动docker服务#

Terminal window
[root@CTFGZ ~]# systemctl enable docker
[root@CTFGZ ~]# systemctl start docker

检查docker服务#

Terminal window
[root@CTFGZ ~]# systemctl status docker
docker.service - Docker Application Container Engine
Loaded: loaded (/usr/lib/systemd/system/docker.service; enabled; preset: disabled)
Active: active (running) since Thu 2025-05-15 20:37:27 CST; 1 day 1h ago
......
[root@CTFGZ ~]# docker version
Client:
Version: 18.09.0
EulerVersion: 18.09.0.346
API version: 1.39
Go version: go1.21.4
Git commit: 270f890
Built: Fri Mar 21 10:03:24 2025
OS/Arch: linux/amd64
Experimental: false
Server:
Engine:
Version: 18.09.0
EulerVersion: 18.09.0.346
API version: 1.39 (minimum version 1.12)
Go version: go1.21.4
Git commit: 270f890
Built: Fri Mar 21 10:02:53 2025
OS/Arch: linux/amd64
Experimental: false
[root@CTFGZ ~]# docker-compose version
Docker Compose version v2.36.0

创建GZCTF项目#

Terminal window
[root@CTFGZ ~]# mkdir /opt/GZCTF
[root@CTFGZ ~]# cd /opt/GZCTF/

配置GZCTF容器端口范围#

因为云主机某些端口占用了,我现在需要指定比赛生成的容器端口在指定范围内。(因为我是内网主机映射到云主机上的所以要这么做)

Terminal window
# 指定端口从31000到31099
[root@CTFGZ GZCTF]# echo "net.ipv4.ip_local_port_range = 31000 31099" >> /etc/sysctl.conf
[root@CTFGZ GZCTF]# sysctl -p

编写GZCTF配置文件#

<Your_postgres_password>数据库密码 <basexxxxx>加密信息 <Your PUBLIC_ENTRY>地址段或者域名

Terminal window
[root@CTFGZ GZCTF]# vim appsettings.json
{
"AllowedHosts": "*",
"ConnectionStrings": {
"Database": "Host=db:5432;Database=gzctf;Username=postgres;Password=<Your_postgres_password>",
"RedisCache": "cache:6379,abortConnect=false"
},
"EmailConfig": {
"SendMailAddress": "",
"UserName": "",
"Password": "",
"Smtp": {
"Host": "localhost",
"Port": 587
}
},
"XorKey": "<basexxxxx>", //自行配置
"ContainerProvider": {
"Type": "Docker", // or "Kubernetes"
"PublicEntry": "<Your PUBLIC_ENTRY>", // or "xxx.xxx.xxx.xxx"
"PortMappingType": "Default",
"EnableTrafficCapture": false,
"DockerConfig": {
"SwarmMode": false,
"Uri": "unix:///var/run/docker.sock"
}
},
"RequestLogging": false,
"DisableRateLimit": true,
"RegistryConfig": {
"UserName": "",
"Password": "",
"ServerAddress": ""
},
"CaptchaConfig": {
"Provider": "None",
"SiteKey": "<Your SITE_KEY>",
"SecretKey": "<Your SECRET_KEY>",
"GoogleRecaptcha": {
"VerifyAPIAddress": "https://www.recaptcha.net/recaptcha/api/siteverify",
"RecaptchaThreshold": "0.5"
}
},
"ForwardedOptions": {
"ForwardedHeaders": 5,
"ForwardLimit": 1,
"TrustedNetworks": ["192.168.12.0/8"]
}
}

配置docker compose文件#

<初始用户密码>密码需要大写字母

Terminal window
[root@CTFGZ GZCTF]# vim docker-compose.yml
services:
gzctf:
image: registry.cn-shanghai.aliyuncs.com/gztime/gzctf:latest
restart: always
environment:
- "LANG=zh_CN.UTF-8"
- "GZCTF_ADMIN_PASSWORD=<初始用户密码>"
ports:
- "80:8080"
volumes:
- "./data/files:/app/files"
- "./appsettings.json:/app/appsettings.json:ro"
- "/var/run/docker.sock:/var/run/docker.sock"
depends_on:
- db
- cache
cache:
# It is recommended to use garnet, you can also use ghcr.nju.edu.cn/microsoft/garnet-alpine:latest
image: redis:alpine
command: ["--bind", "0.0.0.0"]
restart: always
db:
image: postgres:alpine
restart: always
environment:
- "POSTGRES_PASSWORD=<Your_postgres_password>" //记得修改
volumes:
- "./data/db:/var/lib/postgresql/data"

启动GZCTF#

Terminal window
[root@CTFGZ GZCTF]# docker-compose up -d
[+] Running 3/3
Container gzctf-cache-1 Started 0.2s
Container gzctf-db-1 Started 0.3s
Container gzctf-gzctf-1 Started 0.5s

备注#

初始用户:Admin 密码:yml文件<初始用户密码>配置的密码

如果登录Admin初始用户失败,请按下面步骤进行调整 1、在Web界面创建任意用户 2、回到命令行,给创建的用户赋予管理员权限

Terminal window
[root@CTFGZ GZCTF]# docker-compose exec db psql -U postgres
psql (17.5)
Type "help" for help.
postgres=# \c gzctf
You are now connected to database "gzctf" as user "postgres".
gzctf=# UPDATE "AspNetUsers" SET "Role"=3 WHERE "UserName"='创建新的用户名';
UPDATE 1
gzctf=# exit

Caddy#

现在这个步骤就是在云主机上的操作了,我云主机系统是debian <easytier_CTF_ip>这个是配置CTF内网主机上easytier的IP。

Terminal window
root@host:~# curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg
root@host:~# curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | tee /etc/apt/sources.list.d/caddy-stable.list
root@host:~# apt update
root@host:~# apt install -y caddy
root@host:~# vi /etc/caddy/Caddyfile
# ----------------------
# CTF 赛事平台
# ----------------------
ctf.xxx.xxx {
reverse_proxy <easytier_CTF_ip>:80 {
# 精简头部传递(Caddy 默认会传递 X-Forwarded-* 头)
header_up X-Real-IP {remote}
header_up X-Forwarded-Port {server_port}
}
}

刚刚我们说到配置容器范围,现在就要用脚本配置这个端口的映射 <easytier_CTF_ip>这个IP地址要自行修改

Terminal window
root@host:~# cat >> caddy.sh << EOF
# 生成31000-31099端口配置
echo "# 自动生成端口映射配置" >> /etc/caddy/Caddyfile
for port in {31000..31099}; do
cat << EOF >> /etc/caddy/Caddyfile
ctf.leekk.me:$port {
reverse_proxy <easytier_CTF_ip>:$port {
header_up X-Real-IP {remote}
header_up X-Forwarded-Port {server_port}
}
}
EOF
done
EOF

以下是这次部署的样图#

graph TB
classDef public fill:#FFE8D4,stroke:#FF6B00;
classDef private fill:#D5E8D4,stroke:#4CAF50;
classDef tunnel stroke:#666,stroke-dasharray:5;
subgraph 公网环境
Internet[互联网]
客户端(客户端) -->|HTTPS请求\n443端口| 云主机B
end
subgraph 虚拟局域网
云主机B:::public -.->|easytier隧道\n10.144.144.0/24| 内网主机A:::private
end
云主机B -->|反向代理规则\nCaddy: ctf.xxx.com -> 10.144.144.1:80| 内网主机A
内网主机A -->|响应数据| 云主机B
云主机B -->|加密响应| 客户端
style 虚拟局域网 fill:#F0F5FF,stroke:#3366FF
linkStyle 2 stroke:#FF6B00,stroke-width:2px
linkStyle 3 stroke:#4CAF50
linkStyle 4 stroke:#FF6B00
class 云主机B,Internet,客户端 public
class 内网主机A private
CTF院赛平台搭建笔记
http://blog.xeu.asia/posts/实验室折腾/ctf院赛平台搭建笔记/
作者
Xeu
发布于
2025-05-18
许可协议
CC BY-NC-SA 4.0