Comprehensive Installation and Configuration Manual
Developed by: University of Nevada, Las Vegas (UNLV) Cybersecurity Graduate Students
In Partnership with: C2Society - A 501(c)(3) Nonprofit Organization
This comprehensive guide was developed through a collaborative effort between UNLV Cybersecurity graduate students and C2Society, a 501(c)(3) nonprofit organization dedicated to advancing cybersecurity education and awareness.
This guide presents one example approach to setting up CTFd. There are many valid alternatives:
Choose the stack that best fits your experience level and infrastructure requirements.
Before diving into the technical setup, let's understand the key components and technologies you'll encounter:
CTFd is an open-source Capture The Flag framework designed for running cybersecurity competitions. It provides a web-based scoreboard, challenge hosting, team management, and administrative tools for organizing CTF events of any size.
Docker is a containerization platform that packages applications with all their dependencies into portable containers. Think of it like a shipping container for software - everything the application needs to run is included, ensuring it works the same way on any computer.
Docker Compose is a tool for defining and running multi-container Docker applications. CTFd uses it to orchestrate multiple services (web application, database, cache) with a single configuration file called docker-compose.yml.
The CLI is a text-based interface for executing system commands. Instead of clicking buttons in a graphical interface, you type commands to perform actions. In Linux, this is called the terminal or shell, and it's where you'll do most of the server configuration.
Python is the programming language CTFd is built with. It's a versatile, readable language commonly used in cybersecurity tools. CTFd uses the Flask web framework, which is written in Python, to serve the web application.
JavaScript runs in web browsers and makes websites interactive. CTFd uses JavaScript for dynamic features like real-time score updates, challenge submissions, and interactive elements in the user interface.
HTML (HyperText Markup Language) provides the structure of web pages, while CSS (Cascading Style Sheets) controls their appearance. CTFd themes use HTML templates and CSS stylesheets to customize the look and feel of your CTF competition.
A virtual machine (VM) is essentially a computer running inside your computer. It allows you to run a different operating system (like Linux on Windows) without affecting your main system. This is useful for testing and development.
While this guide uses VirtualBox and Kali Linux, you can use:
https://www.virtualbox.org
Install Docker and Docker Compose on your Linux system:
sudo apt update && sudo apt upgrade -y
sudo apt install docker.io docker-compose git -y
sudo yum install docker docker-compose git -y
sudo pacman -S docker docker-compose git
sudo systemctl start docker
sudo systemctl enable docker
sudo usermod -aG docker $USER
Note: After adding your user to the docker group, log out and back in for changes to take effect.
Clone the CTFd repository and set up a local instance for testing:
git clone https://github.com/CTFd/CTFd.git
cd CTFd
head -c 64 /dev/urandom | base64 -w 0 > .ctfd_secret_key
cat > .env << EOF
SECRET_KEY=$(cat .ctfd_secret_key)
DATABASE_URL=mysql+pymysql://ctfd:ctfd@db/ctfd
REDIS_URL=redis://cache:6379
EOF
sudo docker-compose up -d
After starting, access CTFd at http://localhost:8000
in your web browser.
Most popular cloud platform with free tier available and extensive documentation.
Offers $300 free credit for new users and good Kubernetes support.
Simple interface with predictable pricing that's great for beginners.
Enterprise-focused platform with good Windows support and free tier.
Setting | Recommended Value | Notes |
---|---|---|
Instance Type | t2.medium or t3.medium | Minimum 4GB RAM for stable operation |
Operating System | Ubuntu 22.04 LTS | Long-term support, wide compatibility |
Storage | 30GB minimum | More if hosting many Docker challenges |
Security Groups | 22, 80, 443, 8000 | SSH, HTTP, HTTPS, CTFd |
1. Create hosted zone for your domain
2. Add A record pointing to EC2 IP
3. Update nameservers at registrar
1. Add site to Cloudflare
2. Update nameservers at registrar
3. Add A record in Cloudflare dashboard
4. Enable proxy for DDoS protection
sudo apt install nginx certbot python3-certbot-nginx -y
sudo nano /etc/nginx/sites-available/ctfd
Add the following configuration (replace your-domain.com with your actual domain):
server {
listen 80;
server_name your-domain.com;
location / {
proxy_pass http://localhost:8000;
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-Proto $scheme;
}
}
sudo ln -s /etc/nginx/sites-available/ctfd /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl restart nginx
sudo certbot --nginx -d your-domain.com
Follow the prompts to complete SSL setup. Certbot will automatically configure HTTPS for your domain.
cd /opt
sudo git clone https://github.com/CTFd/CTFd.git
cd CTFd
sudo nano .env
Add the following production settings:
SECRET_KEY=$(head -c 64 /dev/urandom | base64 -w 0)
DATABASE_URL=mysql+pymysql://ctfd:ctfd@db/ctfd
REDIS_URL=redis://cache:6379
WORKERS=4
REVERSE_PROXY=true
LOG_FOLDER=/var/log/CTFd
UPLOAD_FOLDER=/var/uploads
sudo docker-compose up -d
To enable email verification and password resets, you must configure an SMTP server.
# In CTFd Admin Panel > Config > Email
Mail Server: smtp.gmail.com
Mail Port: 587
Mail Username: your-email@gmail.com
Mail Password: your-app-specific-password
Mail TLS: Enabled
Mail SSL: Disabled
Mail Sender: your-email@gmail.com
# Sign up for SendGrid (free tier available)
# Get API key from SendGrid dashboard
# In .env file add:
MAIL_SERVER=smtp.sendgrid.net
MAIL_PORT=587
MAIL_USERNAME=apikey
MAIL_PASSWORD=your-sendgrid-api-key
MAIL_USE_TLS=true
MAIL_SENDER=noreply@your-domain.com
# Configure AWS SES in your region
# Verify domain or email addresses
# Get SMTP credentials
# In .env file:
MAIL_SERVER=email-smtp.us-east-1.amazonaws.com
MAIL_PORT=587
MAIL_USERNAME=aws-ses-smtp-username
MAIL_PASSWORD=aws-ses-smtp-password
MAIL_USE_TLS=true
# Enhanced Nginx config with caching and security
server {
listen 443 ssl http2;
server_name your-domain.com;
# SSL configuration (managed by Certbot)
ssl_certificate /etc/letsencrypt/live/your-domain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/your-domain.com/privkey.pem;
# Security headers
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
# Rate limiting
limit_req_zone $binary_remote_addr zone=ctf:10m rate=10r/s;
limit_req zone=ctf burst=20 nodelay;
location / {
proxy_pass http://localhost:8000;
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-Proto $scheme;
# WebSocket support
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
# Timeouts for long-running requests
proxy_connect_timeout 600s;
proxy_send_timeout 600s;
proxy_read_timeout 600s;
}
# Static file caching
location ~* \.(jpg|jpeg|png|gif|ico|css|js)$ {
expires 1y;
add_header Cache-Control "public, immutable";
}
}
Static flag, fixed points, traditional CTF format
Point decay based on solves, encourages speed
Isolated containers per user/team, realistic environments
The Docker-Challenges plugin allows participants to spawn individual containers for each challenge, providing isolated environments for realistic scenarios.
Configure Docker to accept API connections:
sudo systemctl edit docker
Add this configuration:
[Service]
ExecStart=
ExecStart=/usr/bin/dockerd -H fd:// -H tcp://0.0.0.0:2375
Restart Docker:
sudo systemctl restart docker
Verify Docker is listening on the API port:
sudo netstat -tlnp | grep dockerd
You should see: tcp6 0 0 :::2375 :::* LISTEN [PID]/dockerd
The plugin requires the Docker Python SDK to communicate with the Docker daemon:
docker exec -it ctfd_ctfd_1 /opt/venv/bin/pip install docker
Get the gateway IP that CTFd containers use to reach the host:
List Docker networks:
docker network ls
Find your CTFd network (usually ctfd_default or ctfd_internal) and inspect it:
docker network inspect ctfd_internal | grep -A5 -B5 Gateway
Note the Gateway IP (typically something like 172.19.0.1
).
Verify CTFd can reach the Docker API:
docker exec -it ctfd_ctfd_1 /opt/venv/bin/python -c "
import urllib.request
try:
response = urllib.request.urlopen('http://172.19.0.1:2375/version', timeout=5)
print('Connection successful:', response.read()[:100])
except Exception as e:
print('Connection failed:', e)
"
You should see "Connection successful" with Docker version information.
/admin/docker_config
172.19.0.1:2375
(use your gateway IP from Step 3)You'll know the configuration is working when:
Check Docker API is listening:
sudo netstat -tlnp | grep 2375
Verify gateway IP:
docker network inspect ctfd_internal | grep Gateway
Test connection from container:
docker exec -it ctfd_ctfd_1 /opt/venv/bin/python -c "
import urllib.request
response = urllib.request.urlopen('http://YOUR_GATEWAY_IP:2375/version')
print(response.read())
"
Install in correct Python environment:
docker exec -it ctfd_ctfd_1 /opt/venv/bin/pip install docker
Check Docker network configuration:
docker network ls
docker inspect ctfd_ctfd_1 | grep NetworkMode
Use the correct gateway IP for your CTFd network.
After successful configuration:
Participants will see a "Start Docker Instance" button on the challenge page.
Your CTFd installation now supports Docker-based challenges. Participants can spawn individual containers for challenges, making for a more realistic and isolated challenge environment.
Docker challenges exposed to the internet should use TLS for secure communication.
Generate certificates for challenge subdomains:
sudo certbot certonly --standalone -d challenge1.your-domain.com
Mount certificates in your challenge containers:
docker run -d \
-v /etc/letsencrypt:/etc/letsencrypt:ro \
-e CERT_PATH=/etc/letsencrypt/live/challenge1.your-domain.com/fullchain.pem \
-e KEY_PATH=/etc/letsencrypt/live/challenge1.your-domain.com/privkey.pem \
your-challenge-image
For production deployments, consider using a reverse proxy like Nginx to handle SSL termination for all challenge containers.
Import pre-built challenges from GitHub repositories and CTFd export files.
Clone your challenge repository:
git clone https://github.com/your-org/ctf-challenges.git
cd ctf-challenges
To import a CTFd export file:
pip install ctfcli
ctf init
Add challenges programmatically:
ctf challenge add "Challenge Name" \
--category "Web" \
--value 100 \
--flag "CTF{example_flag}"
Via Admin Panel:
# Example challenge structure
challenges/
├── web/
│ ├── challenge1/
│ │ ├── README.md
│ │ ├── challenge.yml
│ │ ├── Dockerfile
│ │ ├── src/
│ │ └── solution/
│ └── challenge2/
├── crypto/
├── pwn/
└── forensics/
# challenge.yml format
name: "SQL Injection 101"
author: "Your Name"
category: Web
description: "Find the flag in the database"
value: 100
type: standard
flags:
- CTF{sql_injection_master}
tags:
- beginner
- sql
hints:
- content: "Try single quotes"
cost: 10
files:
- src/app.py
requirements:
- "Web Basics Tutorial"
CTFd allows you to create a progressive learning path by setting challenge prerequisites. In our implementation, we organized challenges into three difficulty tiers: Easy, Medium, and Hard. Some challenges are gated behind others, requiring participants to complete easier challenges before accessing more difficult ones.
To set up challenge requirements in CTFd:
This approach ensures participants build foundational knowledge before attempting advanced challenges.
We implemented a tutorial system that all participants must complete before accessing the main challenges.
Our tutorial challenge serves multiple purposes:
The tutorial is set as a requirement for all other challenges, ensuring 100% completion rate and rules acknowledgment.
We incorporated multiple choice trivia questions to test theoretical knowledge alongside practical challenges. These questions cover cybersecurity concepts, tools, and best practices. The trivia challenges are worth fewer points but help reinforce learning objectives and provide variety in the competition format.
To encourage comprehensive learning, we implemented bonus challenges that unlock when participants complete specific sets of challenges:
These bonus challenges are hidden by default and only become visible when their requirements are met, providing surprise rewards for dedicated participants.
Our challenge structure follows a clear progression path:
This structure ensures participants develop skills progressively while maintaining engagement through achievable milestones.
The following backup and monitoring scripts are examples and have not been installed or tested in this demo environment. Implement and test thoroughly before using in production.
Create a backup script for your CTFd instance:
#!/bin/bash
BACKUP_DIR="/var/backups/ctfd"
CTFD_DIR="/opt/CTFd"
RETENTION_DAYS=30
mkdir -p $BACKUP_DIR
docker-compose -f $CTFD_DIR/docker-compose.yml exec -T db \
mysqldump -u ctfd -pctfd ctfd > \
$BACKUP_DIR/db-$(date +%Y%m%d-%H%M%S).sql
tar -czf $BACKUP_DIR/uploads-$(date +%Y%m%d-%H%M%S).tar.gz \
$CTFD_DIR/CTFd/uploads/
cp $CTFD_DIR/.env $BACKUP_DIR/env-$(date +%Y%m%d-%H%M%S)
find $BACKUP_DIR -type f -mtime +$RETENTION_DAYS -delete
Consider implementing monitoring solutions such as: