119 lines
3.8 KiB
Markdown
119 lines
3.8 KiB
Markdown
# Deploying the Web CTF challenges to k3s (Traefik)
|
|
|
|
Five web challenges, each a single-container HTTP service on port 80, exposed on
|
|
its own subdomain via a Traefik Ingress.
|
|
|
|
| Host | Challenge | Stack | Flag secret key |
|
|
|----------------------|--------------------------|------------------------|-----------------|
|
|
| `chal1.entysec.com` | Second Track (Easy) | Flask + gunicorn | `chal1-flag` |
|
|
| `chal2.entysec.com` | Second Track Aftermath | Flask + gunicorn | `chal2-flag` |
|
|
| `chal3.entysec.com` | Second Track Reborn | Flask + gunicorn | `chal3-flag` |
|
|
| `chal4.entysec.com` | Big Software Foundation | PHP 7.2 + Apache | `chal4-flag` |
|
|
| `chal5.entysec.com` | Elf's Blog | Node + Chromium + Flask (admin bot) | `chal5-flag` |
|
|
|
|
Each challenge's `entrypoint.sh` reads the `FLAG` env var and writes it to the
|
|
file the app expects — so the flags live only in the `ctf-flags` Secret, never
|
|
baked into an image.
|
|
|
|
## Prerequisites
|
|
|
|
- A running k3s cluster with Traefik installed (k3s ships it by default).
|
|
- `kubectl` configured against the cluster.
|
|
- Docker on the k3s node (to build images), **or** a container registry every
|
|
node can reach (multi-node).
|
|
|
|
## 1. Build images and make them available to k3s
|
|
|
|
k3s uses **containerd**, not the Docker daemon — a `docker build` alone is not
|
|
visible to k3s. `build-images.sh` builds each image and imports it into k3s'
|
|
containerd store. Run it on the k3s node:
|
|
|
|
```bash
|
|
./build-images.sh
|
|
```
|
|
|
|
Multi-node cluster? Use a registry instead of per-node import:
|
|
|
|
```bash
|
|
REGISTRY=registry.entysec.com/ctf ./build-images.sh
|
|
# then prefix the image: names in the chalN-*.yaml manifests with the same registry
|
|
```
|
|
|
|
## 2. Deploy
|
|
|
|
```bash
|
|
./deploy.sh
|
|
```
|
|
|
|
That applies the namespace, the flags Secret, and all five challenges (each a
|
|
Deployment + Service + Ingress), then waits for rollout.
|
|
|
|
Deploy a single challenge:
|
|
|
|
```bash
|
|
kubectl apply -f chal4-big-software-foundation.yaml
|
|
```
|
|
|
|
## 3. Point DNS at the cluster
|
|
|
|
Create DNS records (A/AAAA, or a `*.entysec.com` wildcard) for each host,
|
|
pointing at the IP where Traefik's entrypoint is exposed (the k3s node / load
|
|
balancer). Traefik routes by the `Host` header, so the subdomain must resolve to
|
|
the cluster.
|
|
|
|
Quick local test without DNS:
|
|
|
|
```bash
|
|
curl -H 'Host: chal1.entysec.com' http://<NODE_IP>/
|
|
```
|
|
|
|
## 4. (Optional) HTTPS
|
|
|
|
Two common options:
|
|
|
|
- **cert-manager + Let's Encrypt** — add a `ClusterIssuer`, then a `tls:` block
|
|
referencing a cert Secret on each Ingress and switch the router entrypoint
|
|
annotation to `websecure`.
|
|
- **Traefik default TLS** — set the annotation
|
|
`traefik.ingress.kubernetes.io/router.tls: "true"` and add a `tls:` section.
|
|
|
|
Example TLS-enabled Ingress (with cert-manager):
|
|
|
|
```yaml
|
|
metadata:
|
|
annotations:
|
|
traefik.ingress.kubernetes.io/router.entrypoints: websecure
|
|
cert-manager.io/cluster-issuer: letsencrypt-prod
|
|
spec:
|
|
tls:
|
|
- hosts: [chal1.entysec.com]
|
|
secretName: chal1-tls
|
|
```
|
|
|
|
## Managing flags
|
|
|
|
Flags live in `01-flags-secret.yaml`. After editing, re-apply and restart the
|
|
affected deployment so the new flag is written on boot:
|
|
|
|
```bash
|
|
kubectl apply -f 01-flags-secret.yaml
|
|
kubectl -n ctf rollout restart deploy/chal1-second-track
|
|
```
|
|
|
|
## Tear down
|
|
|
|
```bash
|
|
kubectl delete namespace ctf
|
|
```
|
|
|
|
## Notes
|
|
|
|
- **Elf's Blog** runs gunicorn + a Puppeteer admin bot in one container. It needs
|
|
more memory (limit 1Gi) and gets a tmpfs `/dev/shm`. Chromium already launches
|
|
with `--no-sandbox`, so no extra pod privileges are required. Keep it at
|
|
`replicas: 1` — it persists comments to the container filesystem.
|
|
- The Flask apps read `PORT` (set to 80); the PHP app listens on Apache's
|
|
default 80. All Services target container port 80.
|
|
- To reset a challenge's state (e.g. Elf's Blog comments):
|
|
`kubectl -n ctf rollout restart deploy/chal5-elfs-blog`.
|