Files
CTFd-Whale/docs/install.md
2025-09-19 15:59:08 +08:00

304 lines
8.7 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Installation & Usage Guide
## TLDR
If you never deployed a CTFd instance before:
```sh
curl -fsSL https://get.docker.com -o get-docker.sh
sh get-docker.sh
docker swarm init
docker node update --label-add='name=linux-1' $(docker node ls -q)
git clone https://github.com/CTFd/CTFd --depth=1
git clone https://github.com/frankli0324/ctfd-whale CTFd/CTFd/plugins/ctfd-whale --depth=1
curl -fsSL https://cdn.jsdelivr.net/gh/frankli0324/ctfd-whale/docker-compose.example.yml -o CTFd/docker-compose.yml
# make sure you have pip3 installed on your rig
pip3 install docker-compose
docker-compose -f CTFd/docker-compose.yml up -d
# wait till the containers are ready
docker-compose -f CTFd/docker-compose.yml exec ctfd python manage.py set_config whale:auto_connect_network
```
The commands above tries to install `docker-ce``python3-pip` and `docker-compose`. Make sure the following requirements are satisfied before you execute them:
* have `curl`, `git`, `python3` and `pip` installed
* GitHub is reachable
* Docker Registry is reachable
## Installation
### Start from scratch
First of all, you should initialize a docker swarm and label the nodes
names of nodes running linux/windows should begin with `linux/windows-*`
```bash
docker swarm init
docker node update --label-add "name=linux-1" $(docker node ls -q)
```
Taken advantage of the orchestration ability of `docker swarm`, `ctfd-whale` is able to distribute challenge containers to different nodes(machines). Each time a user request for a challenge container, `ctfd-whale` will randomly pick a suitable node for running the container.
After initializing a swarm, make sure that CTFd runs as expected on your PC/server
Note that the included compose file in CTFd 2.5.0+ starts an nginx container by default, which takes the http/80 port. make sure there's no conflicts.
```bash
git clone https://github.com/CTFd/CTFd --depth=1
cd CTFd # the cwd will not change throughout this guide from this line on
```
Change the first line of `docker-compose.yml` to support `attachable` property
`version '2'` -> `version '3'`
```bash
docker-compose up -d
```
take a look at <http://localhost>(or port 8000) and setup CTFd
### Configure frps
frps could be started by docker-compose along with CTFd
define a network for communication between frpc and frps, and create a frps service block
```yml
services:
...
frps:
image: glzjin/frp
restart: always
volumes:
- ./conf/frp:/conf
entrypoint:
- /usr/local/bin/frps
- -c
- /conf/frps.ini
ports:
- 10000-10100:10000-10100 # for "direct" challenges
- 8001:8001 # for "http" challenges
networks:
default: # frps ports should be mapped to host
frp_connect:
networks:
...
frp_connect:
driver: overlay
internal: true
ipam:
config:
- subnet: 172.1.0.0/16
```
Create a folder in `conf/` called `frp`
```bash
mkdir ./conf/frp
```
then create a configuration file for frps `./conf/frp/frps.ini`, and fill it with:
```ini
[common]
# following ports must not overlap with "direct" port range defined in the compose file
bind_port = 7987 # port for frpc to connect to
vhost_http_port = 8001 # port for mapping http challenges
token = your_token
subdomain_host = node3.buuoj.cn
# hostname that's mapped to frps by some reverse proxy (or IS frps itself)
```
### Configure frpc
Likewise, create a network and a service for frpc
the network allows challenges to be accessed by frpc
```yml
services:
...
frpc:
image: glzjin/frp:latest
restart: always
volumes:
- ./conf/frp:/conf/
entrypoint:
- /usr/local/bin/frpc
- -c
- /conf/frpc.ini
depends_on:
- frps #need frps to run first
networks:
frp_containers:
frp_connect:
ipv4_address: 172.1.0.3
networks:
...
frp_containers: # challenge containers are attached to this network
driver: overlay
internal: true
# if challenge containers are allowed to access the internet, remove this line
attachable: true
ipam:
config:
- subnet: 172.2.0.0/16
```
Likewise, create an frpc config file `./conf/frp/frpc.ini`
```ini
[common]
token = your_token
server_addr = frps
server_port = 7897 # == frps.bind_port
admin_addr = 172.1.0.3 # refer to "Security"
admin_port = 7400
```
### Verify frp configurations
update compose stack with `docker-compose up -d`
by executing `docker-compose logs frpc`, you should see that frpc produced following logs:
```log
[service.go:224] login to server success, get run id [******], server udp port [******]
[service.go:109] admin server listen on ******
```
by seeing this, you can confirm that frpc/frps is set up correctly.
Note: folder layout in this guide:
```
CTFd/
conf/
nginx/ # included in CTFd 2.5.0+
frp/
frpc.ini
frps.ini
serve.py <- this is just an anchor
```
### Configure CTFd
After finishing everything above:
* map docker socket into CTFd container
* Attach CTFd container to frp_connect
```yml
services:
ctfd:
...
volumes:
- /var/run/docker.sock:/var/run/docker.sock
depends_on:
- frpc #need frpc to run ahead
networks:
...
frp_connect:
```
and then clone Whale into CTFd plugins directory (yes, finally)
```bash
git clone https://github.com/frankli0324/CTFd-Whale CTFd/plugins/ctfd-whale --depth=1
docker-compose build # for pip to find requirements.txt
docker-compose up -d
```
go to the Whale Configuration page (`/plugins/ctfd-whale/admin/settings`)
#### Docker related configs
`Auto Connect Network`, if you strictly followed the guide, should be `ctfd_frp_containers`
If you're not sure about that, this command lists all networks in the current stack
```bash
docker network ls -f "label=com.docker.compose.project=ctfd" --format "{{.Name}}"
```
#### frp related configs
* `HTTP Domain Suffix` should be consistent with `subdomain_host` in frps
* `HTTP Port` with `vhost_http_port` in frps
* `Direct IP Address` should be a hostname/ip address that can be used to access frps
* `Direct Minimum Port` and `Direct Maximum Port`, you know what to do
* as long as `API URL` is filled in correctly, Whale will read the config of the connected frpc into `Frpc config template`
* setting `Frpc config template` will override contents in `frpc.ini`
Whale should be kinda usable at this moment.
### Configure nginx
If you are using CTFd 2.5.0+, you can utilize the included nginx.
remove the port mapping rule for frps vhost http port(8001) in the compose file
If you wnat to go deeper:
* add nginx to `default` and `internal` network
* remove CTFd from `default` and remove the mapped 8000 port
add following server block to `./conf/nginx/nginx.conf`:
```conf
server {
listen 80;
server_name *.node3.buuoj.cn;
location / {
proxy_pass http://frps:8001;
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;
}
}
```
## Challenge Deployment
### Standalone Containers
Take a look at <https://github.com/CTFTraining>
In one word, a `FLAG` variable will be passed into the container when it's started. You should write your own startup script (usually with bash and sed) to:
* replace your flag with the generated flag
* remove or override the `FLAG` variable
PLEASE create challenge images with care.
### Grouped Containers
"name" the challenge image with a json object, for example:
```json
{
"hostname": "image",
}
```
Whale will keep the order of the keys in the json object, and take the first image as the "main container" of a challenge. The "main container" will be mapped to frp with same rules from standalone containers
see how grouped containers are created in the [code](utils/docker.py#L58)
## Security
* Please do not allow untrusted people to access the admin account. Theoretically there's an SSTI vulnerability in the config page.
* Do not set bind_addr of the frpc to `0.0.0.0` if you are following this guide. This may enable contestants to override frpc configurations.
* If you are annoyed by the complicated configuration, and you just want to set bind_addr = 0.0.0.0, remember to enable Basic Auth included in frpc, and set API URL accordingly, for example, `http://username:password@frpc:7400`
## Advanced Deployment
To separate the target server (for lunching instance) and CTFd web server with TLS secured docker API, please refer to [this document](advanced.md)