Skip to content

Changes to make DamCTF work #53

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 28 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
374380a
move render_strict helper to lib module
detjensrobert May 1, 2025
b1d1c84
Make sure helm binary is available for cluster-setup
detjensrobert Apr 14, 2025
c9ba519
Handle displaying all errors in cli main instead of in command run funcs
detjensrobert Mar 4, 2025
372b456
Switch to better-documented official external-dns chart
detjensrobert May 1, 2025
cf19204
move extdns user values to end of template
detjensrobert May 1, 2025
506f22e
use challenge domain as cert issuer email
detjensrobert May 1, 2025
6131fa2
fix cert issuer quoting
detjensrobert May 1, 2025
5b1cad2
add ingress aws lb config, request cert for challenge ingresses
detjensrobert May 1, 2025
6d19acc
Use strict render helper for challenge manifest templates
detjensrobert May 1, 2025
1a1ce7a
fix tls config for chal ingress
detjensrobert May 1, 2025
7d4d088
also add AWS LB config opts to tcp services
detjensrobert May 1, 2025
af3aae0
create external-dns cnames instead of alias
detjensrobert May 4, 2025
5c2b144
Add documentation for challenge description template fields
detjensrobert May 5, 2025
6af3086
Full docs on how to write challenge.yaml for chal authors
detjensrobert May 5, 2025
76bb60c
use our ingress class for cert challenges
detjensrobert May 5, 2025
bd3e348
clarify provide is files only
detjensrobert May 6, 2025
d0513ad
Add registry pull credentials to challenge namespaces
detjensrobert May 6, 2025
8977698
use challenge name instead of category-name slug for tcp domains
detjensrobert May 6, 2025
9880f03
Fix flag reference in test chal bar
detjensrobert May 7, 2025
034c11d
render out challenge information to markdown temporarily
detjensrobert May 7, 2025
7cc91f6
add architecture selector for arm challenges
detjensrobert May 7, 2025
76d6139
build container images for the pods arch
detjensrobert May 7, 2025
0933877
fix returned archive path for in-repo asset zips
detjensrobert May 8, 2025
33ab4c4
trim flag whitespace in chal info
detjensrobert May 8, 2025
626fec0
Always pull latest challenge images
detjensrobert May 8, 2025
314443c
fix domains for chals with spaces
detjensrobert May 9, 2025
d91818f
fix hostname template
detjensrobert May 9, 2025
2a94604
Fetch docker.io credentials when building challenge images
detjensrobert May 9, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 21 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ rust-s3 = { version = "0.35.1", default-features = false, features = [
minijinja = { version = "2.6.0", features = ["json"] }
duct = "0.13.7"
fastrand = "2.3.0"
base64ct = { version = "1.7.3", features = ["alloc"] }
docker_credential = "1.3.2"


[dev-dependencies]
Expand Down
199 changes: 199 additions & 0 deletions docs/for-challenge-authors.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,199 @@
# How to write beaverCDS challenge.yaml config

tldr: see [the TCP example](#full-tcp-example) or [the web example](#full-http-example).

### Metadata

Self explanatory.

```yaml
name: yet another pyjail
author: somebody, John Author
```

### Description

Challenge description supports markdown and Jinja-style templating for challenge info.
The Jinja template fields available are:

| Field name | Description |
| ----------- | ----------- |
| `hostname` | The hostname or domain for the challenge
| `port` | The port that the challenge is listening on
| `nc` | Insert the `nc` command to connect to TCP challenges (`nc {{hostname}} {{port}}`)
| `link` | Create a Markdown link to the exposed hostname/port
| `url` | The URL from `link` without the accompanying Markdown
| `challenge` | The full challenge.yaml config for this challenge, with subfields

You probably only want `{{ nc }}` or `{{ link }}`.

Example:

```yaml
description: |
Some example challenge. Blah blah blah flavor text.

In case you missed it, this was written by {{ challenge.author }}
and is called {{ challenge.name }}.

{{ link }} # -becomes-> [example.chals.thectf.com](https://example.chals.thectf.com)
{{ nc }} # -becomes-> `nc example.chals.thectf.com 12345`
```


### Flag

Read flag from file:

```yaml
flag:
file: ./flag
```

### Pods

Defines how any container images for this challenge are built and deployed.

The pod `name` is also used for extracting files, see [Providing files to
users](<for-challenge-authors#Providing files to users>).

`build` works similar to [Docker Compose](https://docs.docker.com/reference/compose-file/build/#illustrative-example),
either:
- a string path to the build context folder
- yaml with explicit `context` path, `dockerfile` path within context folder, and `args` build args \
(only `context`, `dockerfile`, and `args` are supported for the detailed form)

`ports` controls how the container is exposed. This should be a list of what port the container is listening, and how
that port should be exposed to players:
- For TCP challenges, set `expose.tcp` to the subdomain and port: `<subdomain>:<port>`
- For HTTP challenges, set `expose.http` to the subdomain only: `<subdomain>` \
The website domain will automatically be set up with an HTTPS cert.


```yaml
pods:
- name: tcp-example
build: .
replicas: 2
ports:
- internal: 31337
expose:
tcp: thechal:30124 # exposed at thechal.<challenges_domain>:30124

- name: web-example
build:
context: src/
dockerfile: Containerfile
replicas: 2
ports:
- internal: 31337
expose:
http: webchal # exposed at https://webchal.<challenges_domain>
```




This can be omitted if there are no containers for the challenge.

### Providing files to users

Files to give to players as downloads in frontend.

These can be from the challenge folder in the repository, or from the
challenge's built container. These can also be zipped together into one file, or
uploaded separately. These need to be files, directories or globs are not (yet)
supported.

This can be omitted if there are no files provided.

```yaml
provide:
# file from the challenge folder in the repo
- somefile.txt

# multiple files from chal_folder/src/, zipped as together.zip
- as: together.zip
include:
- src/file1
- src/file2
- src/file3

# extract these files from inside of the container image
# for the `main` pod (see previous section)
- from: main
include:
- /chal/notsh
- /lib/x86_64-linux-gnu/libc.so.6

# same as above, but now zipped together
- from: main
as: notsh.zip
include:
- /chal/notsh
- /lib/x86_64-linux-gnu/libc.so.6
```





# Examples

## Full TCP example

```yaml
name: notsh
author: John Author
description: |-
This challenge isn't a shell

{{ nc }}

provide:
- from: main
include:
- /chal/notsh
- /lib/x86_64-linux-gnu/libc.so.6

flag:
file: ./flag

pods:
- name: main
build: .
replicas: 2
ports:
- internal: 31337
expose:
tcp: 30124
```

## Full HTTP example

```yaml
name: bar
author: somebody
description: |
can you order a drink from the webserver?

{{ url }}

difficulty: 1

flag:
file: ./flag

# no provide: section needed if no files

pods:
- name: bar
build:
context: .
dockerfile: Containerfile
replicas: 1
ports:
- internal: 80
expose:
http: bar # subdomain only
```
2 changes: 1 addition & 1 deletion src/access_handlers/docker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ use minijinja;
use tokio;
use tracing::{debug, error, info, trace, warn};

use crate::clients::{docker, render_strict};
use crate::configparser::{get_config, get_profile_config};
use crate::{clients::docker, utils::render_strict};

/// container registry / daemon access checks
#[tokio::main(flavor = "current_thread")] // make this a sync function
Expand Down
5 changes: 5 additions & 0 deletions src/asset_files/challenge_templates/deployment.yaml.j2
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,14 @@ spec:
labels:
rctf/part-of: "{{ slug }}-{{ pod.name }}"
spec:
imagePullSecrets:
- name: "rcds-{{ slug }}-pull"
nodeSelector:
kubernetes.io/arch: {{ pod.architecture }}
containers:
- name: "{{ pod.name }}"
image: "{{ pod_image }}"
imagePullPolicy: Always
ports:
{% for p in pod.ports -%}
- containerPort: {{ p.internal }}
Expand Down
8 changes: 8 additions & 0 deletions src/asset_files/challenge_templates/http.yaml.j2
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

future consideration: do we want a way to use non-letsencrypt ACME?

Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ metadata:
namespace: "rcds-{{ slug }}"
annotations:
app.kubernetes.io/managed-by: rcds
cert-manager.io/cluster-issuer: letsencrypt
spec:
ingressClassName: beavercds
rules:
Expand All @@ -39,3 +40,10 @@ spec:
port:
number: {{ p.internal }}
{% endfor -%}

tls:
- hosts:
{%- for p in http_ports %}
- "{{ p.expose.http }}.{{ domain }}"
{% endfor -%}
secretName: "rcds-tls-{{ slug }}-{{ pod.name }}"
2 changes: 1 addition & 1 deletion src/asset_files/challenge_templates/namespace.yaml.j2
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
apiVersion: v1
kind: Namespace
metadata:
name: rcds-{{ slug }}
name: "rcds-{{ slug }}"
annotations:
app.kubernetes.io/managed-by: rcds
rctf/challenge: "{{ chal.name }}"
Expand Down
15 changes: 15 additions & 0 deletions src/asset_files/challenge_templates/pull-secret.yaml.j2
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
apiVersion: v1
kind: Secret
type: kubernetes.io/dockerconfigjson
metadata:
name: "rcds-{{ slug }}-pull"
namespace: "rcds-{{ slug }}"
stringData:
.dockerconfigjson: |
{
"auths": {
"{{ registry_domain }}": {
"auth": "{{ creds_b64 }}"
}
}
}
10 changes: 9 additions & 1 deletion src/asset_files/challenge_templates/tcp.yaml.j2
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,15 @@ metadata:
app.kubernetes.io/managed-by: rcds
# still use separate domain for these, since exposed LoadBalancer services
# will all have different ips from each other
external-dns.alpha.kubernetes.io/hostname: "{{ slug }}.{{ domain }}"
external-dns.alpha.kubernetes.io/hostname: "{{ name_slug }}.{{ domain }}"

# aws-specific annotations for lb options
service.beta.kubernetes.io/aws-load-balancer-scheme: "internet-facing"
service.beta.kubernetes.io/aws-load-balancer-backend-protocol: tcp
service.beta.kubernetes.io/aws-load-balancer-cross-zone-load-balancing-enabled: "true"
service.beta.kubernetes.io/aws-load-balancer-type: nlb
service.beta.kubernetes.io/aws-load-balancer-manage-backend-security-group-rules: "true"

spec:
type: LoadBalancer
selector:
Expand Down
11 changes: 6 additions & 5 deletions src/asset_files/setup_manifests/external-dns.helm.yaml.j2
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@
rbac:
create: true

{{ provider_credentials }}

# Watch these resources for new DNS records
sources:
- service
Expand All @@ -20,10 +18,10 @@ txtOwnerId: "k8s-external-dns"
txtPrefix: "k8s-owner."

extraArgs:
# ignore any services with internal ips
#exclude-target-net: "10.0.0.0/8"
# special character replacement
txt-wildcard-replacement: star
- --txt-wildcard-replacement=star
# use CNAME instead of ALIAS for alb targets
- --aws-prefer-cname

## Limit external-dns resources
resources:
Expand All @@ -34,3 +32,6 @@ resources:
cpu: 10m

logLevel: debug

# assign last to override any previous values if required
{{ provider_credentials }}
9 changes: 9 additions & 0 deletions src/asset_files/setup_manifests/ingress-nginx.helm.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,15 @@ controller:
ingressClassResource:
name: beavercds

# set variety of annotations needed for the cloud providers

annotations:
service.beta.kubernetes.io/aws-load-balancer-scheme: "internet-facing"
service.beta.kubernetes.io/aws-load-balancer-backend-protocol: tcp
service.beta.kubernetes.io/aws-load-balancer-cross-zone-load-balancing-enabled: "true"
service.beta.kubernetes.io/aws-load-balancer-type: nlb
service.beta.kubernetes.io/aws-load-balancer-manage-backend-security-group-rules: "true"

# nginx values for tcp ports will be set separately in other values file
# this will make it easier for `deploy` to update those values without
# subsequent calls to `cluster-setup` overwriting changes.
Loading