CTFd Setup & Admin Guide

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

Introduction

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.

Important Note: Multiple Deployment Options Available

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.

Table of Contents

Part VI: Advanced Settings

Part I: Preparation & Testing

Understanding the Basics

Before diving into the technical setup, let's understand the key components and technologies you'll encounter:

CTFd Platform

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

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

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.

CLI (Command Line Interface)

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

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

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 & CSS

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.

Virtual Machines

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.

Local Environment Setup

Virtualization Options

While this guide uses VirtualBox and Kali Linux, you can use:

Using VirtualBox (Example)

  1. Download VirtualBox from https://www.virtualbox.org
  2. Install for your operating system
  3. Download your preferred Linux distribution:
    • Kali Linux: Security-focused, includes pentesting tools
    • Ubuntu: User-friendly, great documentation
    • Debian: Stable, minimal installation
  4. Import the VM and allocate at least 4GB RAM

Docker Installation

Install Docker and Docker Compose on your Linux system:

For Ubuntu/Debian/Kali:

sudo apt update && sudo apt upgrade -y
sudo apt install docker.io docker-compose git -y

For CentOS/RHEL:

sudo yum install docker docker-compose git -y

For Arch Linux:

sudo pacman -S docker docker-compose git

Start Docker Service:

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.

Local CTFd Testing

Clone the CTFd repository and set up a local instance for testing:

Clone CTFd Repository:

git clone https://github.com/CTFd/CTFd.git
cd CTFd

Generate Secret Key:

head -c 64 /dev/urandom | base64 -w 0 > .ctfd_secret_key

Create Environment Configuration:

cat > .env << EOF
SECRET_KEY=$(cat .ctfd_secret_key)
DATABASE_URL=mysql+pymysql://ctfd:ctfd@db/ctfd
REDIS_URL=redis://cache:6379
EOF

Start CTFd:

sudo docker-compose up -d

After starting, access CTFd at http://localhost:8000 in your web browser.

Part II: Setting Up Cloud Infrastructure

Choosing a Cloud Provider

AWS EC2

Most popular cloud platform with free tier available and extensive documentation.

Google Cloud

Offers $300 free credit for new users and good Kubernetes support.

DigitalOcean

Simple interface with predictable pricing that's great for beginners.

Azure

Enterprise-focused platform with good Windows support and free tier.

AWS EC2 Setup (Example)

Instance Configuration

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

Domain & DNS Configuration

Using Route 53 (AWS):

1. Create hosted zone for your domain
2. Add A record pointing to EC2 IP
3. Update nameservers at registrar

Alternative: Cloudflare (Free):

1. Add site to Cloudflare
2. Update nameservers at registrar
3. Add A record in Cloudflare dashboard
4. Enable proxy for DDoS protection

SSL/TLS Configuration with Let's Encrypt

Install Nginx and Certbot:

sudo apt install nginx certbot python3-certbot-nginx -y

Create Nginx Configuration:

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;
    }
}

Enable the Site:

sudo ln -s /etc/nginx/sites-available/ctfd /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl restart nginx

Obtain SSL Certificate:

sudo certbot --nginx -d your-domain.com

Follow the prompts to complete SSL setup. Certbot will automatically configure HTTPS for your domain.

Part III: Deploying CTFd

Production CTFd Installation

Clone CTFd to Server:

cd /opt
sudo git clone https://github.com/CTFd/CTFd.git
cd CTFd

Create Production Environment File:

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

Start CTFd:

sudo docker-compose up -d

Email Registration Configuration

Email Setup Required for Registration

To enable email verification and password resets, you must configure an SMTP server.

Option 1: Gmail SMTP (Easy Setup)

# 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

Option 2: SendGrid (Production Recommended)

# 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

Option 3: AWS SES (Scalable)

# 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

Advanced Nginx Configuration

# 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";
    }
}

Part IV: Building CTFd Challenges

Challenge Types Overview

Standard Challenges

Static flag, fixed points, traditional CTF format

Dynamic Challenges

Point decay based on solves, encourages speed

Docker Challenges

Isolated containers per user/team, realistic environments

Docker Challenges Plugin Configuration

The Docker-Challenges plugin allows participants to spawn individual containers for each challenge, providing isolated environments for realistic scenarios.

Prerequisites

Step 1: Configure Docker Daemon for API Access

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

Step 2: Install Docker Python SDK in CTFd Container

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

Step 3: Find Docker Gateway IP

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).

Step 4: Test Docker API Connection

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.

Step 5: Configure CTFd Docker Plugin

  1. Access CTFd Admin Panel and navigate to /admin/docker_config
  2. Enter Configuration:
    • Hostname: 172.19.0.1:2375 (use your gateway IP from Step 3)
    • TLS Enabled: No
    • Leave all certificate fields empty
  3. Click Submit
  4. Verify Success: The "Repositories" section should now show available Docker images instead of "Failed to Connect to Docker"

Success Indicators

You'll know the configuration is working when:

Troubleshooting Docker Plugin Issues

Problem: "Failed to Connect to Docker"

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())
"
Problem: Docker SDK Not Found

Install in correct Python environment:

docker exec -it ctfd_ctfd_1 /opt/venv/bin/pip install docker
Problem: Network Unreachable

Check Docker network configuration:

docker network ls
docker inspect ctfd_ctfd_1 | grep NetworkMode

Use the correct gateway IP for your CTFd network.

Creating Docker Challenges

After successful configuration:

  1. Go to Admin → Challenges → Create Challenge
  2. Select "docker" as challenge type
  3. Choose the appropriate Docker image from the repositories dropdown
  4. Configure challenge settings as normal
  5. Save the challenge

Participants will see a "Start Docker Instance" button on the challenge page.

Security Considerations

Your CTFd installation now supports Docker-based challenges. Participants can spawn individual containers for challenges, making for a more realistic and isolated challenge environment.

TLS Configuration for Docker Challenges

Securing Docker Challenge Containers

Docker challenges exposed to the internet should use TLS for secure communication.

Manual Certificate Management

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.

Importing and Exporting Challenges

Challenge Repository Integration

Import pre-built challenges from GitHub repositories and CTFd export files.

Importing from GitHub Repository

Clone your challenge repository:

git clone https://github.com/your-org/ctf-challenges.git
cd ctf-challenges

To import a CTFd export file:

  1. Go to Admin Panel → Config → Backup
  2. Click "Import"
  3. Upload the .zip file
  4. Select import options:
    • Challenges only
    • Challenges + Flags
    • Full backup (includes users, teams, etc.)

Using CTFd CLI Tool

pip install ctfcli
ctf init

Add challenges programmatically:

ctf challenge add "Challenge Name" \
  --category "Web" \
  --value 100 \
  --flag "CTF{example_flag}"

Exporting Challenges for Backup

Via Admin Panel:

  1. Navigate to Admin → Config → Backup
  2. Click "Export"
  3. Choose export options:
    • Minimal: Challenges and flags only
    • Standard: Include files and hints
    • Full: Everything including submissions

Challenge Development Workflow

# 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"

Part V: CTFd Gamification

Challenge Requirements System

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:

  1. Create your challenges and assign them to categories
  2. Edit each challenge and navigate to the "Requirements" section
  3. Select which challenges must be completed before this one becomes visible
  4. Save your changes

This approach ensures participants build foundational knowledge before attempting advanced challenges.

Tutorial System with Rules Agreement

Mandatory Tutorial Implementation

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.

Knowledge Testing with Multiple Choice

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.

Bonus System Implementation

Category Completion Bonuses

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.

Progressive Difficulty System

Our challenge structure follows a clear progression path:

  1. Tutorial: Mandatory starting point for all participants
  2. Easy Challenges: Fundamental concepts and basic techniques
  3. Medium Challenges: Require combining multiple concepts, only accessible after completing some Easy challenges
  4. Hard Challenges: Complex scenarios requiring advanced skills, gated behind Medium challenges
  5. Bonus Challenges: Special rewards for category completion and achievements

This structure ensures participants develop skills progressively while maintaining engagement through achievable milestones.

Part VI: Advanced Settings

Maintenance & Backup

Disclaimer

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.

Example Backup Script

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

Monitoring Recommendations

Consider implementing monitoring solutions such as: