Deployment guide

CTFd

Overview

CTFd is a popular open-source Capture The Flag competition platform for running security training events, workshops and jeopardy-style competitions. It provides a challenge board, a live scoreboard, individual or team play, solve tracking, hints and a complete administration panel for authoring challenges, flags and pages.

This SecureAnyCloud image runs CTFd 3.8.6 in a dedicated Python virtual environment, served by gunicorn with gevent workers, bound to loopback and reverse-proxied behind nginx. MySQL 8.0 holds durable state and Redis provides caching. First-run setup is completed automatically, and a demo challenge is seeded so the board is a working event. The MySQL root, application database, Flask secret key and CTFd administrator passwords are all rotated per instance at first boot, so nothing ships with a known password.

What you get:

  • CTFd 3.8.6 from the official release at /opt/CTFd, served by gunicorn (gevent, 2 workers)
  • An nginx reverse proxy on port 80 with an unauthenticated static /healthz for load balancer probes
  • MySQL 8.0.x holding the CTFd database (ctfd) with a per-VM application user
  • Redis for CTFd caching and sessions, bound to loopback
  • A single admin account created automatically at first boot, with a per-VM password
  • A seeded demo challenge, so the challenge board and scoreboard are populated on first boot
  • ctfd-firstboot.service, which rotates every secret, completes setup and seeds the demo challenge
  • Host-agnostic URLs: CTFd renders on the VM public IP, on 127.0.0.1, or on any domain you point at it
  • CTFd, nginx, MySQL and Redis enabled and starting automatically on boot
  • 24/7 SecureAnyCloud support

Prerequisites

You need an active Azure subscription, an SSH key, and a VNet with a subnet.

Standard_B2s (2 vCPU, 4 GB RAM) is a comfortable starting size for a small event. Move up to a D2s or D4s size for larger competitions with many concurrent players.

On the network security group, open inbound ports 80 and 443, and port 22 to your management network. MySQL, Redis and the gunicorn app listen only on loopback and are never exposed.

1

Deploy and connect

Launch the image from the Azure Marketplace, then connect over SSH as azureuser:

ssh azureuser@<vm-ip>

Within a few seconds of the instance starting, the first-boot service rotates the credentials, completes the CTFd setup wizard and seeds the demo challenge.

2

Verify the services

Confirm that CTFd, nginx, MySQL and Redis are active. CTFd, MySQL and Redis are bound to loopback only, and nginx is the single ingress on port 80:

sudo systemctl is-active ctfd.service nginx.service mysql.service redis-server.service
sudo ss -ltn | grep -E ':80|:8000|:6379|:3306'
3

Check the versions

CTFd reports its version from the installed package. The runtime is gunicorn on MySQL and Redis:

sudo -u ctfd bash -c "cd /opt/CTFd && /opt/CTFd/venv/bin/python -c 'import CTFd; print(\"CTFd\", CTFd.__version__)'" /opt/CTFd/venv/bin/gunicorn --version mysql --version redis-server --version | awk '{print $1, $3}'
4

Inspect the database

The schema is created and a single administrator account exists, stored with a hashed password, so there is no default or shared login. The seeded demo challenge is present too.

Read the application database password from the root-only credentials file, then inspect the tables, users and challenges:

DB_PASS=$(sudo grep '^CTFD_DB_PASSWORD=' /root/ctfd-credentials.txt | cut -d= -f2-) sudo mysql -uctfd -p"$DB_PASS" ctfd -e "SELECT COUNT(*) AS ctfd_tables FROM information_schema.tables WHERE table_schema='ctfd'"
sudo mysql -uctfd -p"$DB_PASS" ctfd -e "SELECT id,name,type,hidden FROM users"
sudo mysql -uctfd -p"$DB_PASS" ctfd -e "SELECT id,name,category,value,state FROM challenges"
5

Read the administrator password

The per-VM credentials are written to a root-only file. Read it to get the CTFD_ADMIN_USER and CTFD_ADMIN_PASSWORD for signing in:

sudo cat /root/ctfd-credentials.txt

Keep this file secure. Change the administrator password from within CTFd after your first sign-in. The Flask secret key lives in /etc/ctfd/ctfd.env.

6

Sign in

Confirm that CTFd is serving locally. Then browse to http://<vm-ip>/login and sign in as admin with the password from Step 5:

curl -s -o /dev/null -w 'home : HTTP %{http_code}\n' http://127.0.0.1/
curl -s -o /dev/null -w 'healthz : HTTP %{http_code}\n' http://127.0.0.1/healthz
7

Open the challenge board

After you sign in, you land on the challenge board. Each card is a challenge, and the seeded Welcome challenge is worth 100 points. Click a challenge to open it, read the prompt, and submit a flag in the flag{...} format. You author more challenges from the admin panel in Step 9.

8

View the scoreboard

The scoreboard ranks players (or teams) by score and shows a timeline of solves as the event runs. It is public by default so that spectators can follow along, and it updates live as flags are submitted.

9

Open the admin panel

Open the admin panel to run your event. You can create and edit challenges, add flags and hints, manage users and teams, control challenge and scoreboard visibility, set start and end times, and watch statistics. Use it to build out the competition beyond the seeded demo challenge.

10

Components

Component Path

CTFd install /opt/CTFd/

Python venv /opt/CTFd/venv/

Environment (secret key, DB URL) /etc/ctfd/ctfd.env (mode 0640 root:ctfd)

gunicorn unit /etc/systemd/system/ctfd.service

nginx vhost /etc/nginx/sites-available/sac-ctfd

Firstboot script /usr/local/sbin/ctfd-firstboot.sh

MySQL data /var/lib/mysql/

Credentials /root/ctfd-credentials.txt (mode 0600 root:root)

11

Security and next steps

  • Change the administrator password from within CTFd (Admin, then Config, then your account) after your first sign-in, and add real accounts for your players.
  • Enable HTTPS. Install certbot and request a certificate with sudo apt-get install -y certbot python3-certbot-nginx, then sudo certbot --nginx.
  • Restrict the network security group so that ports 80 and 443 are open only to your players or load balancer, and port 22 only to your management network.
  • Author your challenges in the admin panel. Set the event start and end times, and choose challenge, account and score visibility to match how you want to run the competition.
  • Point your domain at the instance. CTFd derives its URLs from the request host, so no rewrite is needed.
  • Patch the OS regularly with sudo apt-get update and sudo apt-get upgrade.

Licensing

CTFd is distributed under the Apache-2.0 licence and is free to use commercially. SecureAnyCloud packages and supports the image, and support is available 24/7 at sales@secureanycloud.com.

All product and company names are trademarks or registered trademarks of their respective holders. Their use does not imply affiliation or endorsement.

Deploy on Azure

Launch CTFd on Ubuntu 24.04 LTS on Azure with 24/7 support from SecureAnyCloud.

View this image

Need Help?

Our support team is available 24/7.

sales@secureanycloud.com

Deploy on Azure