Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
89 commits
Select commit Hold shift + click to select a range
59320a6
Add /admin/api route with moved JSON-API handler
Monster0506 Jul 20, 2026
119e43d
Remove admin/+server.ts so form actions own the /admin route
Monster0506 Jul 20, 2026
712b1c9
Repoint d.view.svelte admin fetches to /admin/api
Monster0506 Jul 20, 2026
2acaeba
Repoint d.users.svelte admin fetch to /admin/api
Monster0506 Jul 20, 2026
cb44b4c
Repoint d.admins.svelte admin fetch to /admin/api
Monster0506 Jul 20, 2026
4f00d7f
Repoint d.teams.svelte admin fetch to /admin/api
Monster0506 Jul 20, 2026
2e3910d
Repoint file_upload.svelte admin fetch to /admin/api
Monster0506 Jul 20, 2026
1436e38
Regenerate baseline drizzle migration cleanly
Monster0506 Jul 20, 2026
8a2da64
Commit drizzle/meta/ migration tracking files
Monster0506 Jul 20, 2026
fd1458b
Add custom migration to seed the default event_config row
Monster0506 Jul 20, 2026
fc84e4b
Add unwired migrate service to docker-compose.yml
Monster0506 Jul 20, 2026
d5f2295
Wire app to depend on migrate completing successfully
Monster0506 Jul 20, 2026
ac3ce2e
Strip schema-creation SQL out of the Postgres image
Monster0506 Jul 20, 2026
37f7c41
Set ssl: false explicitly in drizzle.config.ts
Monster0506 Jul 20, 2026
9378edd
Remove plaintext DB credential logging from drizzle.config.ts
Monster0506 Jul 20, 2026
6ceb4df
Add npm run db:generate script
Monster0506 Jul 20, 2026
b5bf3b1
Add npm run db:migrate script
Monster0506 Jul 20, 2026
9ca8faf
Switch package manager lockfile from npm to bun
Monster0506 Jul 20, 2026
c1405f6
Migrate main Dockerfile and supervisord to bun
Monster0506 Jul 20, 2026
6e28810
Migrate migrate/Dockerfile to bun
Monster0506 Jul 20, 2026
87129cc
Update README dev instructions from npm to bun
Monster0506 Jul 20, 2026
0552d1b
Switch handler's package manager lockfile from npm to bun
Monster0506 Jul 20, 2026
d34e3f2
Migrate handler/Dockerfile and supervisord to bun
Monster0506 Jul 20, 2026
6d0c95f
Add nullable challenge_id column to instance_sessions
Monster0506 Jul 20, 2026
5ce376f
Populate challenge_id when creating an nc/nsjail instance
Monster0506 Jul 20, 2026
98d5332
Add ssh_instance_sessions table
Monster0506 Jul 20, 2026
f8d93a3
Add image_ref column to challenges
Monster0506 Jul 20, 2026
ed3fce4
Add docker_proxy_net network definition
Monster0506 Jul 20, 2026
4542086
Add docker-socket-proxy service
Monster0506 Jul 20, 2026
e0a701f
build challenge's own Dockerfile
Monster0506 Jul 20, 2026
483b2e1
Create ssh-challenge-wrapper Dockerfile.wrapper skeleton
Monster0506 Jul 20, 2026
e0618e4
Add apk/apt-get detection and openssh-server install to wrapper
Monster0506 Jul 20, 2026
5b16e67
Add ctf-player login user creation to wrapper
Monster0506 Jul 20, 2026
c62d435
Point ctf-player's home at the inherited WORKDIR
Monster0506 Jul 20, 2026
f52c882
Add wrapper entrypoint: CTF_PASSWORD -> chpasswd -> exec sshd
Monster0506 Jul 20, 2026
82b7d5c
build the SSH wrapper image
Monster0506 Jul 20, 2026
af9743c
Add registry/docker-compose.yml (stock registry:2)
Monster0506 Jul 20, 2026
a5eb540
Add plain TCP healthcheck to registry
Monster0506 Jul 20, 2026
f22f407
Generate htpasswd from env vars at registry container start
Monster0506 Jul 20, 2026
d0338eb
Refuse to start the registry if credentials are missing
Monster0506 Jul 20, 2026
67a6a1f
Add GetActiveNcInstances query for the admin Instances tab
Monster0506 Jul 21, 2026
b37208d
Build read-only admin Instances tab
Monster0506 Jul 21, 2026
8125e97
Add static nc type-badge column to Instances tab
Monster0506 Jul 21, 2026
016b49a
Add unwired Stop button to Instances tab rows
Monster0506 Jul 21, 2026
39954e6
Wire Instances tab Stop button to handler's /kill
Monster0506 Jul 21, 2026
2e8ff8b
Delete instance_sessions row on successful stop
Monster0506 Jul 21, 2026
7f72e1b
Scaffold ssh-orchestrator service
Monster0506 Jul 21, 2026
1ade670
Implement listing running SSH instance containers via the proxy
Monster0506 Jul 21, 2026
48fa89b
Implement free SSH port picking against the running instance list
Monster0506 Jul 21, 2026
11f70e1
Implement CreateSSHInstance (no expiry logic yet)
Monster0506 Jul 21, 2026
7ed6bd9
Implement /stop_instance
Monster0506 Jul 21, 2026
32718df
Set khi.expires_at label at SSH instance creation time
Monster0506 Jul 21, 2026
a3f8494
Add in-process expiry timer
Monster0506 Jul 21, 2026
cd92276
Add startup reconciliation
Monster0506 Jul 21, 2026
4196583
Wire app onto ssh_orch_net so it can reach ssh-orchestrator
Monster0506 Jul 21, 2026
e11f18f
db.ts: add CreateSSHInstance (no stop-existing guard yet)
Monster0506 Jul 21, 2026
858e1d5
Add stop-existing-first guard to CreateSSHInstance
Monster0506 Jul 21, 2026
c477f61
Add GET /api/sshinstance
Monster0506 Jul 21, 2026
48d667c
Add stop/restart action on /api/sshinstance
Monster0506 Jul 21, 2026
dd3b6db
Add SSH Instance Image field to challenge form
Monster0506 Jul 21, 2026
ca17669
Wire image_ref field to persist into challenges.image_ref
Monster0506 Jul 21, 2026
3ed5b19
Add Launch SSH Instance control, wired to CreateSSHInstance
Monster0506 Jul 21, 2026
4eaa2ad
Render ssh command and password for an active SSH instance
Monster0506 Jul 21, 2026
e084032
Add live countdown bound to the SSH instance's real expires_at
Monster0506 Jul 21, 2026
ce885f3
Add Restart SSH Instance control
Monster0506 Jul 21, 2026
af7e795
Extend admin-instances query to include SSH sessions
Monster0506 Jul 21, 2026
0e59d01
Make Instances tab type badge vary between nc and ssh
Monster0506 Jul 21, 2026
ec45973
Show per-type ID and correct time-remaining in Instances tab
Monster0506 Jul 21, 2026
c428468
Wire SSH row Stop button to ssh-orchestrator's /stop_instance
Monster0506 Jul 21, 2026
423d645
Add SSH_IMAGE_REGISTRY/SSH_REGISTRY_USER/SSH_REGISTRY_PASSWORD
Monster0506 Jul 21, 2026
0e88316
Add IMAGES=1 to docker-socket-proxy's scope
Monster0506 Jul 21, 2026
f47eb94
Implement SSH_IMAGE_PREFIX allowlist check
Monster0506 Jul 21, 2026
f86045a
Implement prefix-and-pull-through-proxy for SSH_IMAGE_REGISTRY
Monster0506 Jul 21, 2026
2ef3856
Fix copy selector for commands
Monster0506 Jul 21, 2026
a83d544
Install nano and vim in the SSH challenge wrapper so players can edit…
Monster0506 Jul 21, 2026
7b4c1d5
Preserve /etc/passwd permissions through wrapper user creation, start…
Monster0506 Jul 21, 2026
8d8b058
Start /opt/khi-daemon in the background if present
Monster0506 Jul 21, 2026
b02e6a7
Give each SSH instance its own private network instead of a shared one
Monster0506 Jul 21, 2026
56e80a3
Web challenge instances
Monster0506 Jul 21, 2026
10ca785
fix svelte-check errrors
Monster0506 Jul 22, 2026
2639045
dropdown select images
Monster0506 Jul 22, 2026
987633d
ui for instances table
Monster0506 Jul 22, 2026
e2c0e3e
proper instance handling
Monster0506 Jul 22, 2026
09910df
postgres error handling
Monster0506 Jul 22, 2026
50e11fd
show web instances in instance table
Monster0506 Jul 22, 2026
7d762f1
filtering of instances
Monster0506 Jul 22, 2026
fe93284
container deletion and configuration
Monster0506 Jul 22, 2026
fa5e78d
update packages to latest versions
Monster0506 Jul 22, 2026
25399f6
allow delete
Monster0506 Jul 22, 2026
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
2 changes: 0 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -27,5 +27,3 @@ vite.config.ts.timestamp-*
uploads/
ctf/
khi_web_logs/
docs/superpowers/
.superpowers/
31 changes: 19 additions & 12 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,28 +1,35 @@
FROM oven/bun:1 AS build

WORKDIR /app
COPY drizzle/ ./drizzle
COPY src/ ./src
COPY static/ ./static
COPY drizzle.config.ts bun.lock package.json svelte.config.js tsconfig.json vite.config.ts .

RUN bun install --frozen-lockfile && \
BETTER_AUTH_URL=http://placeholder \
BETTER_AUTH_SECRET=placeholder \
bun run build

FROM nginx:latest
RUN apt-get update && apt-get install -y supervisor nodejs npm net-tools && rm -rf /var/lib/apt/lists/*
RUN apt-get update && apt-get install -y supervisor net-tools && rm -rf /var/lib/apt/lists/*
COPY --from=build /usr/local/bin/bun /usr/local/bin/bun

# prepare necessary directories
RUN mkdir -p /app

# replace nginx config file
COPY khi.conf /etc/nginx/conf.d/default.conf

# move the svelte app into the container and build it
WORKDIR /app
COPY drizzle/ ./drizzle
COPY src/ ./src
COPY static/ ./static
COPY drizzle.config.ts package-lock.json package.json svelte.config.js tsconfig.json vite.config.ts .

RUN npm install . && \
BETTER_AUTH_URL=http://placeholder \
BETTER_AUTH_SECRET=placeholder \
npm run build
COPY --from=build /app/build ./build
COPY --from=build /app/node_modules ./node_modules
COPY --from=build /app/package.json ./package.json

# copy the automation config for running nginx and the web-app
COPY supervisord.conf /etc/supervisor/conf.d/supervisord.conf

# prep entry script and execute it
COPY entrypoint.sh /root/entrypoint.sh
RUN chmod +x /root/entrypoint.sh
CMD ["/root/entrypoint.sh"]
CMD ["/root/entrypoint.sh"]
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,8 @@ UPLOADS_DIR=

Running the svelte application
```console
npm install .
npm run dev -- --open
bun install
bun run dev -- --open
```

If you'd prefer running the compose locally
Expand Down
640 changes: 640 additions & 0 deletions bun.lock

Large diffs are not rendered by default.

71 changes: 70 additions & 1 deletion docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,66 @@ services:
# handler does not need to be on the same network as the database
- handler_net

docker-socket-proxy:
image: tecnativa/docker-socket-proxy
container_name: docker_socket_proxy_khi
restart: unless-stopped
environment:
CONTAINERS: 1
POST: 1
IMAGES: 1
NETWORKS: 1
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
networks:
- docker_proxy_net

ssh-orchestrator:
container_name: ssh_orchestrator_khi
restart: unless-stopped
build:
context: .
dockerfile: ./ssh-orchestrator/Dockerfile
environment:
DOCKER_PROXY_HOST: docker-socket-proxy
DOCKER_PROXY_PORT: 2375
SSH_MIN_PORT: ${SSH_MIN_PORT}
SSH_MAX_PORT: ${SSH_MAX_PORT}
WEB_MIN_PORT: ${WEB_MIN_PORT}
WEB_MAX_PORT: ${WEB_MAX_PORT}
SSH_INSTANCE_MINUTES: ${SSH_INSTANCE_MINUTES:-45}
SSH_INSTANCE_CPU_NANOS: ${SSH_INSTANCE_CPU_NANOS:-1000000000}
SSH_INSTANCE_MEM_BYTES: ${SSH_INSTANCE_MEM_BYTES:-268435456}
SSH_INSTANCE_PIDS_LIMIT: ${SSH_INSTANCE_PIDS_LIMIT:-128}
WEB_INSTANCE_CPU_NANOS: ${WEB_INSTANCE_CPU_NANOS:-1000000000}
WEB_INSTANCE_MEM_BYTES: ${WEB_INSTANCE_MEM_BYTES:-268435456}
WEB_INSTANCE_PIDS_LIMIT: ${WEB_INSTANCE_PIDS_LIMIT:-128}
SSH_IMAGE_REGISTRY: ${SSH_IMAGE_REGISTRY:-}
SSH_REGISTRY_USER: ${SSH_REGISTRY_USER:-}
SSH_REGISTRY_PASSWORD: ${SSH_REGISTRY_PASSWORD:-}
SSH_IMAGE_PREFIX: ${SSH_IMAGE_PREFIX:-khi-ssh/}
WEB_IMAGE_PREFIX: ${WEB_IMAGE_PREFIX:-khi-web/}
networks:
- ssh_orch_net
- docker_proxy_net

migrate:
container_name: migrate_khi
build:
context: .
dockerfile: ./migrate/Dockerfile
environment:
PG_HOST: postgres
PG_PORT: 5432
PG_USER: ${PG_ADMIN_USER}
PG_PASSWORD: ${PG_ADMIN_PASSWORD}
PG_DATABASE: ${PG_DATABASE}
networks:
- app_internal
depends_on:
postgres:
condition: service_healthy

app:
container_name: web_khi
restart: unless-stopped
Expand Down Expand Up @@ -85,6 +145,10 @@ services:
BIN_UPLOADS_DIR: /app/ctf
CHALLENGE_HOST: ${CHALLENGE_HOST:-ctf.hacksu.com}
TESTING_READ: "C++ is the Best Language, the very best."

SSH_IMAGE_REGISTRY: ${SSH_IMAGE_REGISTRY:-}
SSH_REGISTRY_USER: ${SSH_REGISTRY_USER:-}
SSH_REGISTRY_PASSWORD: ${SSH_REGISTRY_PASSWORD:-}
labels:
- "traefik.enable=true"
- "traefik.http.routers.khi.rule=Host(`ctf.hacksu.com`)"
Expand All @@ -101,9 +165,12 @@ services:
networks:
- app_internal
- handler_net
- ssh_orch_net
depends_on:
postgres:
condition: service_healthy
migrate:
condition: service_completed_successfully

volumes:
khi_psql_data:
Expand All @@ -113,4 +180,6 @@ volumes:

networks:
app_internal:
handler_net:
handler_net:
docker_proxy_net:
ssh_orch_net:
8 changes: 2 additions & 6 deletions drizzle.config.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,5 @@
import { defineConfig } from "drizzle-kit";

console.log(process.env.PG_HOST!);
console.log(process.env.PG_USER!);
console.log(process.env.PG_PASSWORD!);
console.log(process.env.PG_DATABASE!);

export default defineConfig({
schema: "./src/lib/database/auth-schema.ts",
out: "./drizzle",
Expand All @@ -14,6 +9,7 @@ export default defineConfig({
port: Number(process.env.PG_PORT!),
user: process.env.PG_USER!,
password: process.env.PG_PASSWORD!,
database: process.env.PG_DATABASE!
database: process.env.PG_DATABASE!,
ssl: false
}
});
2 changes: 2 additions & 0 deletions drizzle/0001_seed_event_config.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
-- Custom SQL migration file, put your code below! --
INSERT INTO event_config (name, event_start, event_length, site_active) VALUES ('config', NOW(), 7, false) ON CONFLICT (name) DO NOTHING;
2 changes: 2 additions & 0 deletions drizzle/0002_dazzling_paper_doll.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
ALTER TABLE "instance_sessions" ADD COLUMN "challenge_id" integer;--> statement-breakpoint
ALTER TABLE "instance_sessions" ADD CONSTRAINT "instance_sessions_challenge_id_challenges_id_fk" FOREIGN KEY ("challenge_id") REFERENCES "public"."challenges"("id") ON DELETE no action ON UPDATE no action;
12 changes: 12 additions & 0 deletions drizzle/0003_next_kulan_gath.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
CREATE TABLE "ssh_instance_sessions" (
"uid" text PRIMARY KEY NOT NULL,
"challenge_id" integer NOT NULL,
"container_id" text NOT NULL,
"port" integer NOT NULL,
"password" text NOT NULL,
"expires_at" timestamp NOT NULL,
"created_at" timestamp DEFAULT now() NOT NULL
);
--> statement-breakpoint
ALTER TABLE "ssh_instance_sessions" ADD CONSTRAINT "ssh_instance_sessions_uid_user_id_fk" FOREIGN KEY ("uid") REFERENCES "public"."user"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
ALTER TABLE "ssh_instance_sessions" ADD CONSTRAINT "ssh_instance_sessions_challenge_id_challenges_id_fk" FOREIGN KEY ("challenge_id") REFERENCES "public"."challenges"("id") ON DELETE no action ON UPDATE no action;
1 change: 1 addition & 0 deletions drizzle/0004_cooing_kabuki.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ALTER TABLE "challenges" ADD COLUMN "image_ref" text;
9 changes: 9 additions & 0 deletions drizzle/0005_sleepy_celestials.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
CREATE TABLE "web_instances" (
"challenge_id" integer PRIMARY KEY NOT NULL,
"container_id" text NOT NULL,
"port" integer NOT NULL,
"created_at" timestamp DEFAULT now() NOT NULL
);
--> statement-breakpoint
ALTER TABLE "challenges" ADD COLUMN "web_image_ref" text;--> statement-breakpoint
ALTER TABLE "web_instances" ADD CONSTRAINT "web_instances_challenge_id_challenges_id_fk" FOREIGN KEY ("challenge_id") REFERENCES "public"."challenges"("id") ON DELETE no action ON UPDATE no action;
Loading