# 架設自己的CTF CTFd架設教學
作者: [台中教育大學 白帽社](https://hackmd.io/@ntcuhack/index) -sunfrancis12
## 前言
### 伺服器主機
筆者我是在[GCP](https://blog.cloud-ace.tw/google-cloud-platform/products/what-is-gcp/)上面架設環境,並當作網頁伺服器來使用,其實實務上並不建議這樣做,但是當作一個CTFd的平台也已然足夠
但是要注意,GCP最便宜的Computer Engine大概要每月10美(快300台幣)左右,前幾個月可以使用goole提供的抵免額,這點還請讀者自行斟酌
### 防火牆
如果是跟我一樣是使用GCP的讀者,要記得去**GCP的管理介面設定防火牆**,只有在虛擬機裡面設定(ufw)是沒有用的
關於GCP防火牆的設定,可以參考這篇:
> https://ithelp.ithome.com.tw/articles/10263476
### 網頁伺服器 Nginx
關於nginx設定我並沒有設定的十分嚴謹,像是我沒有限至單一使用者傳輸的數據量,因此建議大家還可以自行添加設定,降低被DDOS的風險
### Domain
我是從square space上購買domain的,他第一年購買網域的金額為12美(對,作者我超盤),因此會建議大家去Cloud Flare上面買domain
關於如何將domain綁定主機,可以參考這個影片
> [How To Self Configure Your DNS Settings In Google Domains](https://www.youtube.com/watch?v=Ez3lCBSVHQE&ab_channel=Showit)
### HTTPS SSL 憑證
我是用zerossl來申請憑證的,關於SSL的設定我這裡就不加贅述了,日後有機會筆者在補 (我就爛
這邊推薦這篇文章
> [最全SSL憑證免費申請與安裝教學,ZeroSSL申請教學](https://www.mytechgirl.com/tw/how-to/add-ssl-to-website-free-mtg6688.html)
## 環境和使用工具
### 主機
* [GCP](https://cloud.google.com/)
### CTF平台
* [CTFd](https://github.com/CTFd/CTFd)
### 網頁框架
* [Flask](https://flask.palletsprojects.com/en/2.3.x/)
### 資料庫
* [MariaDB](https://mariadb.org/) or MySQL
### 網頁伺服器
* [nginx](https://www.nginx.com/)
* [gunicorn](https://gunicorn.org/)
### 其他
* [gevent](https://www.gevent.org/)
## CTFd 安裝
這裡按照CTFd官方提供的流程
先下載 CTFd的官方檔案
```
git clone https://github.com/CTFd/CTFd.git
cd CTFd
```
下載完後,執行`prepare.sh`來自動安裝所需檔案
```
prepare.sh
```
或者可以使用`requirements.txt`來安裝
```
pip install -r requirements.txt
```
### 啟動CTFd
等安裝過程都跑完後,測試CTFd是否安裝順利(Flask 預設會跑在5000 port)
```
flask run
```
也可以使用`python`執行
```
python server.py
```
使用`python3`執行
```
python3 server.py
```
## CTFd背景執行
由於上述的啟動方式只適用於Debug,不夠穩定,因此不適合放在伺服器上當作一個服務
要讓他穩定運行,我們需要借助gunicorn
### gunicorn
安裝gunicorn
```
sudo apt install gunicorn
```
啟動 ctfd
```
sudo gunicorn -w 2 -b 0.0.0.0:8000 "CTFd:create_app()" --daemon
```
`-b` 代表要執行的port,(0.0.0.0代表任何人都可以連線)
`-w` 代表使用的[CPU線程](https://zhuanlan.zhihu.com/p/86855590)
`--daemon` 移到背景執行
### gnuicorn debug
查詢 gunicorn 背景執行
```
ps -ef | grep gunicorn
```
清除所有 gunicorn
```
sudo pkill gunicorn
```
> 參考自: [[GCP教學-Python] #5使用Gunicorn將API背景執行](https://andy6804tw.github.io/2020/04/10/gcp-gunicorn/#%E7%B5%90%E6%9D%9F%E8%83%8C%E6%99%AF%E5%9F%B7%E8%A1%8C)
>
### gevent
為了提高執行效率,建議安裝套件 [gevent](https://hustyichi.github.io/2019/04/14/dive-into-gevent/)
```
pip install gevent
```
啟動 ctfd
```
sudo gunicorn -w 2 -b 0.0.0.0:8000 --worker-class="gevent" "CTFd:create_app()" --daemon
```
`--worker-class="gevent"` 使用gevent
## 創建資料庫
我們須要創建資料庫,來儲存使用者的資料和題目的資訊
我這裡使用的是MariaDB,其指令和架構都和MySQL一樣
### MariaDB基本設定
下載 MariaDB
```
sudo apt update
sudo apt install mariadb-server mariadb-client
```
創建資料庫的`root`帳號
```
mysqladmin -u root password {你的密碼}
```
接下來他會問你一些權限的設定問題,請讀者自行選擇
登入帳號,指令格式如下
```
mysql -u {使用者} -p {資料庫名稱}
```
以`root`帳號為例:
```
mysql -u root -p mysql
```
### 建立資料庫
我們需要創建一個資料庫,來存放CTFd的設定
```
create database {資料庫名稱};
```
我這邊創立了一個`test`的資料庫
```
create database ctfd;
```
可以使用來以下指令列出所有資料庫
```
show databases;
```

跳到指定的資料庫
```
use {資料庫名稱};
```
我這裡跳到ctfd為例:
```
use ctfd;
```

> 引用自: [mysql命令行的基本用法(通用linux和windows)](https://registerboy.pixnet.net/blog/post/21684093)
### 新增使用者(可選)
登入`root`帳號
```
mysql -u root -p
```
建立一個使用者
```
CREATE USER '使用者'@'localhost' IDENTIFIED BY '使用者密碼';
```
以我為例:
```
CREATE USER 'ctfd_user'@'localhost' IDENTIFIED BY 'xxxxxxx';
```
給予使用者特定資料庫的所有權限
```
GRANT ALL PRIVILEGES ON 資料庫名稱.* TO '使用者'@'localhost';
```
以我為例:
```
GRANT ALL PRIVILEGES ON ctfd.* TO 'ctfd_user'@'localhost';
```
測試是否可以連到`database`
```
mysql -u ctfd_user -p
```
> 引用自: [MySQL 新增使用者及建立資料庫權限](https://registerboy.pixnet.net/blog/post/21684093)
## CTFd 連結資料庫
打開 `CTFd/config.py`
```
cd CTFd/
sudo vim config.py
```
找到並修改`SQLALCHEMY_DATABASE_URI`的value
```
SQLALCHEMY_DATABASE_URI = 'mysql+pymysql://使用者:密碼@localhost/資料庫名稱?charset=utf8'
```
以我為例:
```
SQLALCHEMY_DATABASE_URI = 'mysql+pymysql://ctfd_user:xxxxxxxx@localhost/資料庫名稱?charset=utf8'
```
> 參考自: [mariadb 默认root密码_debian10 + mariadb + gunicorn + nginx + CTFd 超详细部署攻略以及易错点](https://blog.csdn.net/weixin_39630140/article/details/110596699)
>
### 驗證資料庫
回到`CTFd`資料夾,重啟ctfd
```
python3 serve.py
```
連線到`database`
```
mysql -u ctfd_user -p ctfd
```
把資料庫的`table`印出來看是否有內容
```
show tables;
```

## Nginx 設定
下載`Nginx`
```
sudo apt-get update
sudo apt-get install nginx
```
啟動`Nginx`
```
systemctl start nginx
```
查看`Nginx`狀態
```
systemctl status nginx
```

### 設定防火牆(視情況)
允許防火牆通過`Nginx`使用的`port`(80,443)
```
sudo ufw allow 'Nginx Full'
```
或者直接允許`port`(80,443)
```
sudo ufw allow 80
sudo ufw allow 443
```
檢查是否開通
```
sudo ufw status numbered
```
*其他防火牆的設定我這裡就不加贅述了*
### Nginx伺服器設定
先調成`root`權限,之後就指令前就不用加`sudo`了
```
su -
```
進到`sites-enabled`資料夾
```
cd /etc/nginx/sites-enabled/
```
讀取`default`,修改內容
```
vim default
```
以下是我的nginx設定內容,可以做為參考
```
listen 80 default_server;
listen [::]:80 default_server
#root /var/www/html;
#填入你的域名,沒有的話就直接default server就好了
server_name www.ntcuhacker.com;
location / {
# First attempt to serve request as file, then
# as directory, then fall back to displaying a 404.
#try_files $uri $uri/ =404;
proxy_pass http://localhost:8000;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $http_x_forwarded_proto;
}
```
`proxy_pass`是反向代理,可以將使用者導向其他服務,由於我們當初ctfd是架設在8000`port`,因此這裡將使用者導向至`http://localhost:8000`
下面的設定則是將傳給`/`網址的使用者的IP,協定,封包,完整的傳給`http://localhost:8000`
```
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $http_x_forwarded_proto;
```
按`esc`,並輸入`:wq`儲存修改
### 驗證,以及上架nginx
驗證一下設定檔是否有誤
```
sudo nginx -t
```

重新reload`nginx`服務,更新設定檔
```
systemctl reload nginx
```
或者重啟`nginx`服務
```
systemctl restart nginx
```
## 最終測試
試試看在瀏覽器輸入自己主機的外部ip或者domain

