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

http://rcds.redpwn.com/schemas/challenge.yaml

type

object

properties

  • id

Override the automatically generated id for this challenge. You should avoid setting this whenever possible.

#/definitions/domainsafe-name

  • name

The name of the challenge

type

string

  • author

The author(s) of the challenge

oneOf

type

string

type

array

items

type

string

  • description

Description of the challenge. It is in Markdown format and will be processed with Jinja.

type

string

  • category

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

  • flag

The flag for the challenge.

oneOf

type

string

type

object

properties

  • file

File to load the flag from. The file should contain one line with only the flag.

type

string

  • regex

type

string

format

regex

  • value

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

  • visible

Whether or not this challenge should be shown on the scoreboard.

type

boolean

default

True

  • provide

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

  • file

Path to the file to provide

type

string

  • as

Name of file as shown to competitors

type

string

  • deployed

Whether or not this challenge’s containers should be deployed

type

boolean

default

True

  • containers

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

  • image

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

  • build

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

  • context

Path to the build context

type

string

  • dockerfile

Path to the Dockerfile within the build context

type

string

  • args

Build arguments to be passed to the build. Please write numbers as strings to avoid ambiguity from number formatting

type

object

additionalProperties

type

string

  • replicas

Number of replicas of this container to run. Set to 1 for stateful applications.

type

integer

minimum

1

default

1

  • environment

Environment variables to set within the container. Please format all values as strings. Keys without values are not supported.

type

object

additionalProperties

type

string

  • ports

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

  • resources

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

  • limits

Compute resource limits for this container. Using more of a resource than the limit is not allowed.

type

object

properties

  • cpu

CPU usage limits for this container - 1 unit corresponds to 1 CPU second per (wall-clock) second.

#/definitions/cpu-value

  • memory

Memory usage limits for this container.

#/definitions/memory-value

  • requests

Compute resource requets for this container.

type

object

properties

  • cpu

CPU usage requests for this container - 1 unit corresponds to 1 CPU second per (wall-clock) second.

#/definitions/cpu-value

  • memory

Memory usage requests for this container.

#/definitions/memory-value

  • expose

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

  • target

The port number on the container this rule targets.

type

integer

  • tcp

The external port number to expose, treating this port as raw TCP.

type

integer

  • http

Configuration to expose this port as HTTP.

oneOf

The hostname to expose; this is a subdomain name.

type

string

type

object

properties

  • raw

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

  • domainsafe-name

type

string

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

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