challenge.yaml — Challenge Config¶
The file challenge.yaml defines the configuration for a challenge within an
rCDS project. .yml and .json files are also supported.
Basics¶
id — the identifier for this challenge. Must be unique project wide. This
key is set automatically from the name of the directory the challenge is in;
unless you have a very good reason to, don’t set this in challenge.yaml.
author – a string or list of strings containing the authors for this
challenge.
description – self-explanatory. It is in Markdown format and will be
processed with Jinja. See Templating for more details.
category – self-explanatory. If the challenge directory is exactly two
directories deep (for example, /pwn/chall, where / is the project root),
this is set from the parent directory of the challenge’s directory (“pwn” in the
previous example). We recommend organizing your challenges in a
category/chall structure.
flag — the flag for the challenge. If it is a string, then the flag is set
to the string verbatim. Otherwise, if flag.file is set, the flag is loaded
from the specified file (relative to the challenge root), and stripped of
leading and trailing whitespace. If flag.regex is set, the flag is anything
matching the given regex. A warning is emitted if the flag contains multiple
lines (usually this is from an improperly configured flag file).
provide — an array of files to provide to competitors as downloads. The
files can either be a string, in which case they are interpreted as the path to
the file, or an object with the file and as properties; these properties
define the path and the displayed name of the file, respectively.
value — point value of this challenge. Meaning is defined by the
scoreboard backend.
visible — if set to false, the scoreboard backend will act as if this
challenge does not exist.
Warning
Most scoreboard backends will delete any challenges that were created by
rCDS but now no longer exist—switching visible to false after the
challenge has already been deployed may cause solves to be lost.
Deployment¶
In rCDS, you first define all of the containers that your challenge needs to run, and then declare how you want them exposed to the world.
deployed — whether or not this challenge’s containers should be deployed.
Defaults to true.
Containers¶
The containers key is an object whose keys are the names of the containers
this challenge creates. These containers can either use an existing image, or
specify a path to a Dockerfile to build from. Each container must declare all
ports that need to be connected to, both from other containers and by
competitors; which ports are exposed to competitors are specified
separately. Containers from the same challenge can
connect to each other via a DNS lookup of their names; for example, if a
container app is defined, another container can connect to any of app’s
declared ports by looking up the name app.
Whether a container needs to be rebuilt is determined by looking at every file
in the Docker build context. Thus, it is very important that you include only
what is necessary in the build context by using a .dockerignore file; at
minimum, challenge.yaml should be excluded to prevent needing to rebuild the
container when the challenge’s description is updated.
image — the tag of an existing image to run
build — settings for building this container. If it is a string, then it
is the path to the Docker build context (the directory where a Dockerfile is).
It can also be an object for advanced configuration:
build.context — path to the Docker build context.
build.dockerfile — path to the Dockerfile, relative to the build context
root.
build.args — Docker build args to set when building the container.
Key-value object.
ports — a list of integers of the port numbers this container listens on.
If anything needs to connect to a port on the container, list it here.
replicas — number of replicas of this container to run (on backends that
support it). Defaults to 1. Leave at 1 for stateful containers.
environment — key-value object of environment variables to set.
resources — resource limits on the container. See Kubernetes’s
documentation on the format of this value (only cpu and memory are
implemented).
Expose¶
The top-level expose key defines all of the ports on containers that should be exposed to competitors. It is an object
whose keys correspond to the names of defined containers, and whose values are
arrays of port objects. These objects each describe how one port should be
exposed.
target — the port on the container that this rule is targeting.
tcp — if specified, this port should be treated as TCP. The value is the
port at which it is exposed on, on the challenge host.
http — if specified, this port should be treated as HTTP, and will be
reverse proxied with TLS termination. The value is a string, the subdomain name
on which the challenge will be hosted. Alternatively, it can be an object with a
raw key, in which case http.raw contains the FQDN that the challenge
will be served on. When using http.raw, rCDS will handle the virtual
hosting, however as a challenge author, you will need to coordinate with your
infrastructure admin on setting up TLS and DNS records.
Templating¶
Challenge descriptions are rendered using Jinja. The contents of the
challenge’s config is available on the challenge object in the Jinja
environment. Some fields are altered with more concrete versions of their
contents—for example, the http key on expose port objects will contain
the fully-qualified domain name, instead of just the prefix. Container backends
will also add a host key to a TCP expose port, which contains the host at
which that port will be accessible.
Note
An example configuration:
# challenge.yaml
...
description: |
1: {{ challenge.expose.main[0].http }}
2: {{ challenge.expose.main[1].host }}:{{ challenge.expose.main[1].tcp }}
containers:
main:
ports: [1337, 1338]
expose:
main:
- target: 1337
http: leet
- target: 1338
tcp: 31253
Assuming the container backend is hosted on example.com, the description would render as:
1: leet.example.com
2: example.com:31253
There are also shortcuts available for the most common use-case: a single
exposed port. host is the hostname under which the port is accessible.
link will automatically create a Markdown link to the exposed port, and
url will create just the URL without the accompanying Markdown. This works
for both HTTP and TCP ports, since you may want to expose a challenge which
breaks behind a reverse proxy as TCP. For TCP ports, there is also port,
which is the exposed port number of the port, and nc, which
will create a nc command to connect to the challenge—it is equivalent to
nc {{ host }} {{ port }}.
Reference¶
type |
object |
||||||
properties |
|||||||
|
Override the automatically generated id for this challenge. You should avoid setting this whenever possible. |
||||||
#/definitions/domainsafe-name |
|||||||
|
The name of the challenge |
||||||
type |
string |
||||||
|
The author(s) of the challenge |
||||||
oneOf |
type |
string |
|||||
type |
array |
||||||
items |
type |
string |
|||||
|
Description of the challenge. It is in Markdown format and will be processed with Jinja. |
||||||
type |
string |
||||||
|
Category of the challenge. If not provided, defaults to the parent directory of the challenge (e.g. if this file is located at /pwn/chall1/challenge.yaml, the category will default to ‘pwn’). |
||||||
type |
string |
||||||
|
The flag for the challenge. |
||||||
oneOf |
type |
string |
|||||
type |
object |
||||||
properties |
|||||||
|
File to load the flag from. The file should contain one line with only the flag. |
||||||
type |
string |
||||||
|
type |
string |
|||||
format |
regex |
||||||
|
The point value of the challenge, or full point value for a dynamically-scored challenge; the precise meaning is defined by the scoreboard backend being used. |
||||||
type |
integer |
||||||
minimum |
0 |
||||||
|
Whether or not this challenge should be shown on the scoreboard. |
||||||
type |
boolean |
||||||
default |
True |
||||||
|
Static files (that are in the repository already on disk) to provide to competitors |
||||||
type |
array |
||||||
items |
oneOf |
Path to the file to provide |
|||||
type |
string |
||||||
type |
object |
||||||
properties |
|||||||
|
Path to the file to provide |
||||||
type |
string |
||||||
|
Name of file as shown to competitors |
||||||
type |
string |
||||||
|
Whether or not this challenge’s containers should be deployed |
||||||
type |
boolean |
||||||
default |
True |
||||||
|
Containers to be deployed for this challenge. The key of each container is its name, where the container can be found via DNS lookup at runtime from other containers within this challenge. |
||||||
type |
object |
||||||
additionalProperties |
type |
object |
|||||
properties |
|||||||
|
The image tag for this container. If ‘build’ is not specified, the container will be pulled (e.g. containers for services like a database found on dockerhub). If ‘build’ is specified, this overrides the ‘name’ (default the name of the directory specified in ‘build’) in the image tag template defined globally in the project. |
||||||
type |
string |
||||||
|
oneOf |
Path to the directory containing a Dockerfile to build for this container. |
|||||
type |
string |
||||||
Complex build configuration object roughly mirroring that of docker-compose.yml. |
|||||||
type |
object |
||||||
properties |
|||||||
|
Path to the build context |
||||||
type |
string |
||||||
|
Path to the Dockerfile within the build context |
||||||
type |
string |
||||||
|
Build arguments to be passed to the build. Please write numbers as strings to avoid ambiguity from number formatting |
||||||
type |
object |
||||||
additionalProperties |
type |
string |
|||||
|
Number of replicas of this container to run. Set to 1 for stateful applications. |
||||||
type |
integer |
||||||
minimum |
1 |
||||||
default |
1 |
||||||
|
Environment variables to set within the container. Please format all values as strings. Keys without values are not supported. |
||||||
type |
object |
||||||
additionalProperties |
type |
string |
|||||
|
Port numbers (as integers) on this container to expose to other containers within this challenge. If a port is supposed to be exposed to the Internet, make sure it is specified here, and add it to the top level ‘expose’ key. This key exists to ensure Kubernetes services have the correct ports configured on each service. Due to restrictions with Docker Compose / Docker Swarm, remapping ports as can be done with Kubernetes services is not possible. |
||||||
type |
array |
||||||
items |
type |
integer |
|||||
|
Compute resource requests and limits for this container. This follows the same format as Kubernetes’s resources property on container specs. Not all features are supported by all backends (though limits should work on most). |
||||||
type |
object |
||||||
properties |
|||||||
|
Compute resource limits for this container. Using more of a resource than the limit is not allowed. |
||||||
type |
object |
||||||
properties |
|||||||
|
CPU usage limits for this container - 1 unit corresponds to 1 CPU second per (wall-clock) second. |
||||||
#/definitions/cpu-value |
|||||||
|
Memory usage limits for this container. |
||||||
#/definitions/memory-value |
|||||||
|
Compute resource requets for this container. |
||||||
type |
object |
||||||
properties |
|||||||
|
CPU usage requests for this container - 1 unit corresponds to 1 CPU second per (wall-clock) second. |
||||||
#/definitions/cpu-value |
|||||||
|
Memory usage requests for this container. |
||||||
#/definitions/memory-value |
|||||||
|
Ports on containers to expose to the Internet. Keys correspond to the key of the container that the rule is targeting. |
||||||
type |
object |
||||||
additionalProperties |
type |
array |
|||||
items |
type |
object |
|||||
properties |
|||||||
|
The port number on the container this rule targets. |
||||||
type |
integer |
||||||
|
The external port number to expose, treating this port as raw TCP. |
||||||
type |
integer |
||||||
|
Configuration to expose this port as HTTP. |
||||||
oneOf |
The hostname to expose; this is a subdomain name. |
||||||
type |
string |
||||||
type |
object |
||||||
properties |
|||||||
|
The raw hostname to expose (use when you need the port to not be on the shared parent domain). Deployment backends will not handle DNS records for this. |
||||||
type |
string |
||||||
definitions |
|||||||
|
type |
string |
|||||
pattern |
^[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?$ |
||||||
|
oneOf |
type |
string |
||||
pattern |
^[0-9]+m$ |
||||||
type |
number |
||||||
|
oneOf |
type |
string |
||||
pattern |
^[0-9]+[KMGTE]i?$ |
||||||
type |
number |
||||||
Raw schema:
$schema: http://json-schema.org/draft-07/schema#
$id: http://rcds.redpwn.com/schemas/challenge.yaml
definitions:
domainsafe-name:
type: string
# k8s dns label names allows a max length of 63 characters
pattern: "^[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?$"
cpu-value:
oneOf:
- type: string
pattern: "^[0-9]+m$"
- type: number
memory-value:
oneOf:
- type: string
pattern: "^[0-9]+[KMGTE]i?$"
- type: number
type: object
properties:
# Basic challenge details
id:
$ref: "#/definitions/domainsafe-name"
description: >-
Override the automatically generated id for this challenge. You should
avoid setting this whenever possible.
name:
type: string
description: >-
The name of the challenge
author:
description: >-
The author(s) of the challenge
oneOf:
- type: string
- type: array
items:
type: string
description:
type: string
description: >-
Description of the challenge. It is in Markdown format and will be
processed with Jinja.
category:
type: string
description: >-
Category of the challenge. If not provided, defaults to the parent
directory of the challenge (e.g. if this file is located at
/pwn/chall1/challenge.yaml, the category will default to 'pwn').
flag:
description: >-
The flag for the challenge.
oneOf:
- type: string
- type: object
properties:
file:
type: string
description: >-
File to load the flag from. The file should contain one line with
only the flag.
regex:
type: string
format: regex
# Exactly one of (file, regex) allowed
oneOf:
- required: [file]
- required: [regex]
value:
type: integer
description: >-
The point value of the challenge, or full point value for a
dynamically-scored challenge; the precise meaning is defined by the
scoreboard backend being used.
minimum: 0
visible:
type: boolean
description: >-
Whether or not this challenge should be shown on the scoreboard.
default: true
# Static assets
provide:
type: array
description: >-
Static files (that are in the repository already on disk) to provide to competitors
items:
oneOf:
- type: string
description: >-
Path to the file to provide
- type: object
properties:
file:
type: string
description: >-
Path to the file to provide
as:
type: string
description: >-
Name of file as shown to competitors
required:
- file
- as
# Runtime (containers)
deployed:
type: boolean
description: >-
Whether or not this challenge's containers should be deployed
default: true
containers:
type: object
description: >-
Containers to be deployed for this challenge. The key of each container
is its name, where the container can be found via DNS lookup at runtime
from other containers within this challenge.
additionalProperties:
type: object
properties:
image:
type: string
description: >-
The image tag for this container. If 'build' is not specified, the
container will be pulled (e.g. containers for services like a
database found on dockerhub). If 'build' is specified, this
overrides the 'name' (default the name of the directory specified in
'build') in the image tag template defined globally in the project.
build:
oneOf:
- type: string
description: >-
Path to the directory containing a Dockerfile to build for this container.
- type: object
description: >-
Complex build configuration object roughly mirroring that of
docker-compose.yml.
properties:
context:
type: string
description: >-
Path to the build context
dockerfile:
type: string
description: >-
Path to the Dockerfile within the build context
args:
type: object
description: >-
Build arguments to be passed to the build. Please write
numbers as strings to avoid ambiguity from number formatting
additionalProperties:
type: string
required:
- context
replicas:
type: integer
description: >-
Number of replicas of this container to run. Set to 1 for stateful
applications.
default: 1
minimum: 1
environment:
type: object
description: >-
Environment variables to set within the container. Please format all
values as strings. Keys without values are not supported.
additionalProperties:
type: string
ports:
type: array
description: >-
Port numbers (as integers) on this container to expose to other
containers within this challenge. If a port is supposed to be
exposed to the Internet, make sure it is specified here, and add it
to the top level 'expose' key.
This key exists to ensure Kubernetes services have the correct
ports configured on each service. Due to restrictions with Docker
Compose / Docker Swarm, remapping ports as can be done with
Kubernetes services is not possible.
items:
type: integer
resources:
type: object
description: >-
Compute resource requests and limits for this container. This
follows the same format as Kubernetes's resources property on
container specs.
Not all features are supported by all backends (though limits should
work on most).
properties:
limits:
type: object
description: >-
Compute resource limits for this container. Using more of a
resource than the limit is not allowed.
properties:
cpu:
description: >-
CPU usage limits for this container - 1 unit corresponds to
1 CPU second per (wall-clock) second.
$ref: "#/definitions/cpu-value"
memory:
description: >-
Memory usage limits for this container.
$ref: "#/definitions/memory-value"
requests:
type: object
description: >-
Compute resource requets for this container.
properties:
cpu:
description: >-
CPU usage requests for this container - 1 unit corresponds to
1 CPU second per (wall-clock) second.
$ref: "#/definitions/cpu-value"
memory:
description: >-
Memory usage requests for this container.
$ref: "#/definitions/memory-value"
anyOf:
# Either 'image' or 'build' must be specified
- required:
- image
- required:
- build
propertyNames:
$ref: "#/definitions/domainsafe-name"
expose:
type: object
description: >-
Ports on containers to expose to the Internet. Keys correspond to the key
of the container that the rule is targeting.
additionalProperties:
type: array
items:
type: object
properties:
target:
type: integer
description: >-
The port number on the container this rule targets.
tcp:
type: integer
description: >-
The external port number to expose, treating this port as raw TCP.
http:
description: >-
Configuration to expose this port as HTTP.
oneOf:
- type: string
description: >-
The hostname to expose; this is a subdomain name.
- type: object
properties:
raw:
type: string
description: >-
The raw hostname to expose (use when you need the port
to not be on the shared parent domain). Deployment backends
will not handle DNS records for this.
oneOf:
- required: [raw]
# Exactly one of (http, tcp) allowed
oneOf:
- required: [http]
- required: [tcp]
propertyNames:
$ref: "#/definitions/domainsafe-name"
required:
- name
- description