Writing challenges
The challenge format we use is based on a subset of the compose specification (the format docker compose uses). As such, writing challenges should feel intuitive if you've used Docker Compose before.
Your challenges should be placed in the challs subdirectory of the CTF repository. The challenge is described by a docker-compose.yml or docker-compose.yml.plftera (See the section on the preprocessor for more details).
This document will not describe everything Docker Compose offers, but will instead focus on the differences and extensions our format has.
First: Services and Volumes are the only supported objects. Networks, Secrets and Configs will be ignored.
Defining metadata
The platform extends the compose format with custom metadata that defines your CTF challenge.
To do this, it adds a new key x-ctf-metadata to the compose file. The metadata is structed like this:
x-ctf-metadata:
name: My challenge name
authors:
- Author 1
- Author 2
description_md: |
In this challenge, you're supposed to find the flag.
Check [CTFTime](https://ctftime.org/) to learn more.
flag_validation_fn:
// Optional, you can call setFlagValidationFunction with an arbitrary function (flag: string) => boolean that validates a flag and returns true if it's valid
// You MUST either define a flag validation function or a flag
setFlagValidationFunction((flag) => {
return flag.startsWith("PLFANZY{") && flag.endsWith("}");
});
flag: PLFANZEN{MY_FLAG}
categories: # Should only contain categories defined in the event metadata
- misc
- rev
attachments:
- path/to/your/file # Path, relative to the compose file
release_time: Option<u64>
end_time: Option<u64>
auto_publish_src: true # Optional, defaults to false, more details in the handout section
difficulty: hard # Should be a difficulty defined in the event metadata
data_pvc_size: 100Mb # More details later
additional_metadata: # Additional data you can freely put anything in, optional - May be useful for your points calculationUnless explained otherwise by a comment, fields are required.
The data PVC
If you use relative mounts to your compose file in any service, where you mount a subdir of (./data) a data PVC will be created. It contains everything you put into that data directory.
service properties
The following table gives you information on which properties are and aren't supported.
| Key | Support state | Notes |
|---|---|---|
attach | Ignored | Not relevant on Kubernetes |
build | Not supported | Planned for the future |
blkio_config | Not supported | |
cpu_count | Supported | |
cpu_percent | Not supported | |
cpu_shares | Not supported | |
cpu_period | Not supported | |
cpu_quota | Not supported | |
cpu_rt_runtime | Not supported | |
cpu_rt_period | Not supported | |
cpus | Supported | |
cpuset | Not supported | |
cap_add | Supported | If this is used, a VM will be created instead of a container. You will have these capabilities within the VM. |
cap_drop | Supported | |
cgroup | Not supported | |
cgroup_parent | Not supported | |
command | Supported | |
configs | Not supported | |
container_name | Not supported | |
credential_spec | Not supported | Mostly for Windows containers, which we don't have at the moment |
depends_on | Ignored | We can not enforce startup order easily in Kubernetes |
deploy | Partially Supported | Only replicas and labels are supported |
develop | Ignored | Not relevant on Kubernetes |
device_cgroup_rules | Not supported | |
devices | Not supported | Currently, disabled for security reasons |
dns | Supported | |
dns_opt | Supported | |
dns_search | Supported | |
domain_name | Supported | |
entrypoint | Supported | |
env_file | Supported | Files must be inside the working directory |
environment | Supported | |
expose | Supported | |
extends | Not supported | |
annotations | Supported | Mapped to Pod annotations (not Deployment) |
external_links | Not supported | |
extra_hosts | Supported | |
group_add | Supported | |
healthcheck | Not supported | We can consider adding this later |
hostname | Supported | |
image | Supported | |
init | Supported | Uses tini via init container + wrapper |
ipc | Not supported | |
uts | Not supported | |
isolation | Not supported | This is mostly for Windows containers |
labels | Supported | Mapped to Pod labels (not Deployment) |
links | Not supported | |
logging | Not supported | |
network_config | Not supported | |
mac_address | Not supported | |
mem_limit | Supported | |
mem_reservation | Supported | |
mem_swappiness | Not supported | |
memswap_limit | Not supported | |
oom_kill_disable | Not supported | |
oom_score_adj | Not supported | |
pid | Not supported | |
pids_limit | Not supported | This is a node-level setting in Kubernetes, not per-container |
platform | Not supported | |
ports | Supported | |
privileged | Supported | Creates a VM instead of a real container. You don't have access to host devices, but everything else you would have in a privileged container. |
profiles | Not supported | |
pull_policy | Supported | Build is currently not supported, like build, it is planned for the future |
read_only | Supported | |
restart | Ignored | Kubernetes only supports Always for Deployments |
runtime | Supported | If the container uses privileged or certain capabilities, Kata Containers will be used instead of this |
scale | Supported | |
secrets | Not supported | |
security_opt | Not supported | |
shm_size | Supported | |
stdin_open | Supported | |
stop_grace_period | Supported | |
stop_signal | Supported | |
storage_opt | Not supported | |
sysctls | Not supported | |
tmpfs | Supported | |
tty | Supported | |
ulimits | Not supported | |
user | Partially Supported | Only user IDs are supported, not usernames |
userns_mode | Not supported | For security reasons |
volumes | Partially Supported | Only tmpfs, bind mounts inside the working directory, and named volumes are supported |
volumes_from | Not supported | |
working_dir | Supported |
Network policies
In addition, we declare the x-ctf-network-policy on services.
As soon as you add a policy, all traffic not permitted will be dropped.
A network policy looks like this:
services:
main:
image: ubuntu:24.04
x-ctf-network-policy:
# Incoming policies are currently not supported
outgoing:
rules:
- other_party: ClusterDNS # One of Challenge, ClusterDNS, World
ports: # Optional
- port: 53
protocol:
- UDP
- TCPvolume properties
For volumes, we don't support most properties. The only way to define volumes is the following format:
volumes:
volname:
x-size: 50Mi # The size of the generated volumeOther properties apart from x-size are ignored.