DownUnderCTF 2023 обслужил больше 2000 команд на 68 заданиях, выдержал пиковые 32 100 запросов в секунду и обошёлся в $876 AUD за две недели на Google Cloud - включая полноценное тестовое окружение. За время соревнования было создано 4579 изолированных инстансов заданий (данные из публичного writeup организаторов). В тот же период я поднимал инфраструктуру для университетского CTF на 120 человек - один VPS за 2000 рублей, CTFd в Docker Compose, Nginx на фронте. Оба события прошли стабильно. Разница не в "правильности" стека, а в точном понимании, когда хватает одного сервера, а когда без Kubernetes не выжить.
Русскоязычных материалов по технической стороне развёртывания CTF платформы почти нет. Есть описания сетевых топологий на ESXi и гайды по организации мероприятий, но пошаговых инструкций по CTFd установке и настройке, конфигурации kCTF с nsjail-изоляцией и мониторингу под реальной нагрузкой - нет. Закрываем пробел.
CTFd или kCTF: что выбрать для развёртывания CTF платформы
Первая ошибка начинающих организаторов - путать платформу управления (scoreboard) и среду выполнения заданий (challenge runtime). CTFd решает первую задачу: показывает задания, принимает флаги, ведёт рейтинг. kCTF решает вторую: разворачивает контейнеры заданий с изоляцией между участниками. Они не конкуренты - они работают в паре. DownUnderCTF использует оба одновременно.
CTFd - опенсорсная веб-платформа для Jeopardy-CTF. Проект живой: репозиторий CTFd/CTFd на GitHub, тысячи звёзд, регулярные релизы. DownUnderCTF 2023 использовал версию 3.6.0 и отметил заметное улучшение стабильности по сравнению с предыдущими релизами. Есть коммерческий SaaS-вариант - Hosted CTFd с автоматическим деплоем заданий через Docker-registry.
kCTF - Kubernetes-шаблон от Google для развёртывания CTF-заданий с изоляцией через nsjail. Документация описывает его как "template for deploying tasks using Kubernetes that uses nsjail for isolation between players""Шаблон развёртывания заданий использует Kubernetes, который использует nsjail для изоляции между игроками". Скорборда тут нет - для фронтенда всё равно нужен CTFd или аналог. Проект поддерживается Google, используется в Google CTF.
Критерии выбора и ограничения каждого подхода
Ключевой вопрос: нужна ли per-connection изоляция между участниками? Если задание предполагает RCE (pwn, web с shell), участник может повредить окружение другим - сбросить флаг, убить процесс, попытаться выйти из контейнера (Escape to Host, T1611, Privilege Escalation по MITRE ATT&CK).| Критерий | CTFd + Docker на VPS | kCTF + GKE | Когда что выбирать |
|---|---|---|---|
| Масштаб участников | До 300-500 | 500-5000+ | Зависит от доли hosted-заданий |
| Per-user изоляция | Нет нативно, нужен Klodd | Нативно через nsjail | RCE-задания - только с изоляцией |
| Стоимость за событие | $10-50 (VPS) | $200-900 (GKE) | DownUnderCTF: $876 AUD за 2 недели |
| Время развёртывания | 30-60 минут | 4-8 часов | Нужен опыт с Kubernetes |
| Автомасштабирование | Вертикальное, вручную | Горизонтальное, автоматическое | При непредсказуемом числе участников |
| Healthcheck заданий | cron + скрипты | Встроено в k8s Deployment | Критично для событий >24 часов |
| Защита от container escape | Docker defaults | nsjail + seccomp + user namespaces | PWN-категория - максимальная изоляция |
CTFd + Docker на VPS не тянет, когда участников больше 500 и задания жрут CPU/RAM, когда нужна автоматическая ротация упавших инстансов, или когда в категории PWN есть задания с полноценным RCE.
kCTF + GKE - перебор, когда событие на 50-100 человек в университете (overhead от Kubernetes не оправдан), когда нет опыта с
kubectl и Terraform (время на обучение превысит время на проведение CTF), или когда задания статические - OSINT, crypto без серверной части.По документации kCTF, при использовании GKE нужно контролировать квоты GCP-проекта: каждое задание с Load Balancer потребляет внешний IP, и при 30+ заданиях дефолтные квоты могут быть исчерпаны. Разворачивайте kCTF заранее - убедиться в достаточности квот и увеличить репутацию аккаунта для автоматического одобрения запросов.
CTFd Docker Compose деплой: установка и настройка
Требования к окружению
- ОС: Ubuntu 22.04+ или Debian 12+ (рекомендуется), CentOS 8+, любой GNU/Linux с Docker Engine
- RAM: минимум 2 ГБ для CTFd + MySQL + Redis (до 100 участников), рекомендуется 4-8 ГБ для 200-500 участников
- CPU: 2 vCPU минимум, 4 vCPU рекомендуется при >200 участниках
- Диск: 20 ГБ SSD минимум (задания-файлы + база MySQL)
- Сеть: публичный IP, открытые порты 80/443, домен с DNS-записью
- Зависимости: Docker Engine 24+, Docker Compose v2, certbot для TLS
docker-compose.yml. Базовая конфигурация - три сервиса: приложение, MySQL и Redis для кеширования. Клонируем: git clone https://github.com/CTFd/CTFd.git && cd CTFd.Перед запуском -
SECRET_KEY. Это криптографический ключ сессий. Если переменная не указана, CTFd сгенерирует ключ автоматически и сохранит в persistent volume (директория .data). Для явного задания - передайте SECRET_KEY через секцию environment в docker-compose.yml. Сгенерировать можно так: python3 -c "import os; print(os.urandom(32).hex())". DATABASE_URL и REDIS_URL тоже задаются через environment (в дефолтном compose уже настроены).
YAML:
# docker-compose.yml - ключевые секции CTFd
services:
ctfd:
image: ctfd/ctfd:latest
ports:
- "8000:8000"
environment:
- SECRET_KEY=${SECRET_KEY}
- DATABASE_URL=mysql+pymysql://ctfd:ctfd@db/ctfd
- REDIS_URL=redis://cache:6379
depends_on:
- db
- cache
restart: always
docker compose up -d. CTFd доступен на порту 8000. Первый визит открывает мастер настройки - имя соревнования, учётные данные администратора, режим (team/user).DownUnderCTF на масштабе 2000+ команд отказался от контейнерной базы данных в пользу CloudSQL MySQL и Cloud Redis - разумный шаг при их нагрузке. Для мероприятий до 500 участников контейнерные MySQL и Redis в том же compose-файле работают без проблем. Я проводил CTF на 300 человек с такой конфигурацией - ни одного инцидента с базой.
Reverse proxy, TLS и CTFd плагины настройка
Выставлять CTFd без TLS - прямой путь к перехвату сессий. Nginx или Traefik спереди закрывают три задачи: TLS-терминация, rate limiting, кеширование статики.Конфигурация Nginx:
proxy_pass http://127.0.0.1:8000; с проксированием заголовков X-Forwarded-For, X-Forwarded-Proto, Host. TLS через Let's Encrypt: certbot --nginx -d ctf.example.com. В CTFd включите REVERSE_PROXY=true через переменную окружения или в config.py (значение обрабатывается через Werkzeug ProxyFix). Без этого платформа не увидит реальные IP участников, и rate limiting по IP превратится в фикцию.Rate limiting - не опция, а необходимость. По данным DownUnderCTF 2023, в середине соревнования случился всплеск нагрузки - фактический DoS со стороны участников. Кластер автомасштабировался и выдержал, но после включения rate limits на Cloudflare нагрузка вернулась к норме. Конфигурация в Nginx:
limit_req_zone $binary_remote_addr zone=ctfd:10m rate=10r/s; в блоке http и limit_req zone=ctfd burst=20 nodelay; в location /. CTFd как публичное приложение (T1190, Initial Access) должен быть защищён от автоматизированного перебора и flood-атак (T1499.002, Service Exhaustion Flood).Доступ к admin-панели CTFd (NIST CSF PR.AA-01 - управление учётными данными авторизованных пользователей) - ограничьте
/admin по IP через Nginx allow/deny или через VPN. Не поленитесь - на реальных CTF я видел, как участники пытались брутить admin-панель.Из CTFd плагинов: динамическое начисление баллов (decay scoring) встроено в CTFd 3.x, first blood bonus и кастомные challenge types устанавливаются копированием в директорию
CTFd/plugins/ и перезапуском контейнера через docker compose restart ctfd.kCTF Kubernetes CTF платформа и nsjail изоляция
Архитектура kCTF: три слоя изоляции
kCTF строится на трёх уровнях, и понимание каждого критично для правильного развёртывания:Уровень 1 - nsjail. Sandbox, работающий в режиме LISTEN и форкающий отдельное окружение на каждое TCP-соединение. По документации kCTF, nsjail использует Linux User Namespaces и требует явного перечисления файлов, доступных внутри sandbox. Каждое соединение изолировано - участник с RCE не может повлиять на другого. Бонусом - поддержка Proof of Work (PoW): вычислительная задача перед подключением, которую можно включить на лету при обнаружении злоупотреблений (Compute Hijacking, T1496.001 по ATT&CK - например, когда кто-то решил помайнить на вашей инфраструктуре).
Код:
# nsjail.cfg - пример для PWN-задания
mode: LISTEN
port: 1337
time_limit: 30
rlimit_as_type: SOFT
rlimit_cpu_type: SOFT
rlimit_nproc: 1
mount { src: "/chroot" dst: "/" is_bind: true }
mount { src: "/dev/null" dst: "/dev/null" is_bind: true }
Уровень 3 - Kubernetes Deployment. Шаблон для создания Pod'ов с заданным числом реплик и лимитами на CPU/RAM. Без Load Balancer задания недоступны извне - и это преимущество: можно деплоить и тестировать задание до публикации.
Развёртывание и управление кластером
Создание кластера:kctf cluster create --domain-name ctf.example.com. По документации kCTF, при использовании поддомена нужны NS-записи, указывающие на DNS Google, чтобы задания были доступны по адресам вида *.subdomain.example.com.Для production переведите хотя бы часть нод на обычные:
kctf cluster resize --min-nodes 1 --max-nodes 3 --num-nodes 1 --machine-type n2-standard-4 (без флага spot). Spot-ноды (заменившие устаревшие Preemptible VM в GKE) хороши для резерва при пиках - они на 60-91% дешевле обычных, но могут быть вытеснены в любой момент. Представьте: финал CTF, последний час, и GCP забирает ваши ноды. Не надо так.Деплой задания:
kctf chal create mychal && kctf chal start. Проверка: kctf chal status - показывает состояние Deployment и результат healthcheck. Если healthcheck падает, участник видит потерю соединения - первое, что проверяйте при жалобах на "нестабильное задание".Для соответствия Kubernetes STIG (DISA) в контексте CTF: ограничьте RBAC до минимума необходимых прав, не открывайте API server для участников, примените Network Policy для сегментации трафика между Pod'ами заданий. Задания не должны видеть друг друга по сети - иначе получите цепочку компрометаций через lateral movement между тасками.
Docker изоляция CTF заданий: shared и instanced модели
Помимо статических заданий (файлы для скачивания), в CTF-инфраструктуре выделяют два типа remote-заданий:
Shared remote - все участники подключаются к одному инстансу. Подходит для заданий без состояния: crypto-сервер, web-задание без persistent storage, задачи на анализ протоколов. Один
docker run, один порт, минимум ресурсов.Instanced remote - каждый участник получает отдельный инстанс. Необходим для заданий с RCE, file write, модификацией конфигурации - всего, что позволяет участнику удалить флаг, убить процесс или сломать задание для остальных.
DownUnderCTF создал 4579 изолированных инстансов - причём большинство для заданий beginner/easy. Это важная деталь: именно начинающие участники чаще ломают общие инстансы непреднамеренно. Человек получил shell и начал
rm -rf / "для эксперимента" - знакомо?Для instanced-заданий без kCTF существует Klodd - интеграция для rCTF, разворачивающая задания на Kubernetes по запросу участника. Требует GKE-кластер. Альтернатива - tiny-instancer для меньших масштабов. При работе на одном хосте без кластера - каждое задание запускается через
docker run --memory=256m --cpus=0.5 --pids-limit=50 --read-only с tmpfs для записываемых директорий.Защита от container escape на CTF-инфраструктуре
Container escape (Escape to Host, T1611) - реальная угроза, не теоретическая. На CTF-инфраструктуре участники целенаправленно ищут уязвимости, и sandbox - легитимная цель. Связанные техники по MITRE ATT&CK: Container Administration Command (T1609) - при доступе к Docker socket, Build Image on Host (T1612) - при возможности сборки образов, Container and Resource Discovery (T1613) - разведка внутри контейнера.Чеклист защиты (проверено на практике):
- Docker socket (
/var/run/docker.sock) - никогда не монтировать в контейнер задания. Это полный контроль над хостом. Монтирование docker.sock - самая частая ошибка, которую я вижу у начинающих организаторов --security-opt=no-new-privileges- запрет escalation привилегий внутри контейнера- Metadata endpoint - блокировать
169.254.169.254через iptables на уровне хоста. Документация rCTF прямо предупреждает: доступ к metadata на AWS/GCP даёт учётные данные облачного аккаунта - Отдельный хост - CTFd и задания не на одном VPS. Документация rCTF: "One should avoid reusing the same VPS as for the CTF platform""Избегайте использование одного и того же VPS в одной CTF платформе."
- Resource limits -
--memory,--cpus,--pids-limitпредотвращают fork bomb и исчерпание ресурсов хоста (Service Exhaustion Flood, T1499.002) - Read-only filesystem -
--read-onlyс tmpfs для/tmpи других записываемых путей - nsjail внутри Docker - даже без kCTF, обёртка через nsjail внутри контейнера значительно уменьшает поверхность атаки
CTF сервер мониторинг нагрузка: Prometheus, Grafana и алертинг
Без мониторинга вы узнаете о проблемах от участников в чате - а это уже потеря нескольких минут соревновательного времени. Prometheus + Grafana - стандартный стек, работающий и для VPS-деплоя, и для Kubernetes. Формирование baseline до события (NIST CSF DE.AE-01) - обязательный шаг: прогоните нагрузочный тест через
wrk или locust против CTFd с ожидаемым количеством одновременных пользователей и зафиксируйте нормальные QPS и latency. Без baseline вы не отличите аномалию от нормы.Ключевые метрики и алерты
Платформа (CTFd): HTTP QPS и latency (p50/p95/p99) - пики на старте, при выкладке новых заданий и перед финалом. DownUnderCTF фиксировал характерные пики в три момента: старт CTF и первая волна заданий, выкладка второй волны, финал. HTTP 5xx error rate - CTFd исторически страдал от утечки соединений к базе данных, что проявлялось как массовые 500-ошибки. В версии 3.6.0 стало лучше. Алерт на 5xx rate >1% - минимальный порог для реагирования.Задания: CPU и memory per container (cAdvisor встроен в kubelet, для Docker - метрики через
/metrics endpoint), количество active containers (резкий рост может означать abuse), network I/O per container (аномальный исходящий трафик - потенциальный breakout или майнинг).Инфраструктура: disk I/O и использование (участники могут заполнить диск через write-примитивы), node CPU/RAM для Kubernetes - основа для автомасштабирования.
Prometheus подключается как сервис в compose-файле или запускается отдельно с конфигом targets. Grafana использует Prometheus как datasource - community-дашборды для Docker и Kubernetes доступны в реестре Grafana.
Уровни защиты от злоупотреблений: Nginx
limit_req (10-20 r/s per IP - разумный порог), Cloudflare или аналогичный CDN для L7 DDoS-защиты (для CTF с публичным доступом), Kubernetes Network Policy для сегментации Pod'ов заданий, встроенный rate limiting CTFd на попытки сабмита флага (настраивается в admin panel).Чеклист развёртывания CTF инфраструктуры
Перед стартом соревнования - пройти по списку. Формат пригоден для передачи команде инфраструктуры:SECRET_KEYCTFd сгенерирован криптографически, не дефолтное значение- TLS настроен через Let's Encrypt, HTTP-to-HTTPS редирект активен
- Rate limiting включён на reverse proxy - 10-20 r/s per IP
- Docker socket не смонтирован ни в один контейнер задания
- Metadata endpoint (169.254.169.254) заблокирован на уровне хоста
- Каждый контейнер задания имеет лимиты:
--memory,--cpus,--pids-limit - Бекап базы MySQL настроен - минимум каждые 30 минут во время события
- Prometheus собирает метрики CTFd и контейнеров заданий
- Алерт на 5xx error rate >1% настроен в Alertmanager или Grafana
- Нагрузочный тест проведён с ожидаемым числом одновременных пользователей
- Для instanced-заданий: healthcheck подтверждает решаемость задания
- DNS TTL снижен до 300 секунд - возможность быстрой миграции
- CTFd и задания на разных хостах или в разных namespace в k8s
- Admin panel CTFd ограничена по IP или через VPN
Масштабирование CTF платформы: от VPS к кластеру
📚 Часть контента скрыта. Этот материал доступен участникам сообщества с рангом One Level или выше
Получить доступ просто — достаточно зарегистрироваться и проявить активность на форуме
Получить доступ просто — достаточно зарегистрироваться и проявить активность на форуме
Большинство CTF-организаторов переусложняют инфраструктуру. За несколько лет проведения соревнований от 50 до 500 участников я регулярно наблюдаю одну и ту же картину: команды тратят непропорционально много времени на настройку Kubernetes в ущерб подготовке заданий. А должно быть ровно наоборот - участники приходят решать таски, а не восхищаться вашим кластером. Если вы проводите первый или второй CTF - начинайте с CTFd на одном VPS. Docker Compose, Nginx, Let's Encrypt, базовый мониторинг. Разворачивается за час и покрывает 90% сценариев. kCTF нужен, когда вы уже провели три-четыре мероприятия, понимаете свой масштаб, столкнулись с реальными проблемами изоляции и готовы вложить время в Kubernetes.
Отдельная боль - healthcheck заданий. DownUnderCTF хотели, но за 2023 год так и не внедрили автоматическую проверку решаемости через sidecar-контейнеры с exploit-скриптами. kCTF это поддерживает нативно, и это единственная фича, ради которой стоит рассматривать kCTF даже при небольшом масштабе. Сломанное задание, которое никто не может решить два часа - худший сценарий для организатора. Ручной мониторинг не масштабируется: вы не проверите 30 заданий каждые 15 минут. А healthcheck-контейнер, прогоняющий solve-скрипт - проверит. Формула на бумаге понятна, но decay-scoring и healthcheck по-настоящему начинают работать только когда сам прогоняешь сабмишены на живом стенде. Готовый стенд для отработки есть на HackerLab.pro (https://hackerlab.pro) - российская CTF-платформа с 12 категориями заданий от web до forensics, после регистрации доступны таски всех уровней сложности.
Если вы строите CTF-инфраструктуру и не знаете, с чего начать - берите чеклист из этой статьи и один VPS. Kubernetes никуда не денется, а задания для участников важнее идеальной инфраструктуры.
Последнее редактирование модератором: