diff --git a/.env.example b/.env.example index 8108943..5660022 100644 --- a/.env.example +++ b/.env.example @@ -35,6 +35,7 @@ MAX_PORT= # File uploads UPLOADS_DIR= BIN_UPLOADS_DIR= +PUBLIC_MAX_UPLOAD_SIZE_MB=50 FLAG_ENCRYPTION_KEY= diff --git a/Dockerfile b/Dockerfile index 17392e4..8e12d2e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -12,14 +12,13 @@ RUN bun install --frozen-lockfile && \ bun run build FROM nginx:latest -RUN apt-get update && apt-get install -y supervisor net-tools && rm -rf /var/lib/apt/lists/* +RUN apt-get update && apt-get install -y supervisor net-tools gettext-base && 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 +COPY khi.conf.template /etc/nginx/khi.conf.template WORKDIR /app COPY --from=build /app/build ./build diff --git a/docker-compose.yml b/docker-compose.yml index b86e976..a01caf5 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -142,7 +142,10 @@ services: UPLOADS_DIR: /app/uploads BIN_UPLOADS_DIR: /app/ctf JAIL_CONF_DIR: /app/nsjail_confs - + + PUBLIC_MAX_UPLOAD_SIZE_MB: ${PUBLIC_MAX_UPLOAD_SIZE_MB:-50} + BODY_SIZE_LIMIT: ${PUBLIC_MAX_UPLOAD_SIZE_MB:-50}M + CHALLENGE_HOST: ${CHALLENGE_HOST:-ctf.hacksu.com} FLAG_ENCRYPTION_KEY: ${FLAG_ENCRYPTION_KEY} TESTING_READ: "C++ is the Best Language, the very best." diff --git a/entrypoint.sh b/entrypoint.sh index ce54966..14ab374 100644 --- a/entrypoint.sh +++ b/entrypoint.sh @@ -7,6 +7,10 @@ chown -R www-data:www-data "/app/build" mkdir -p "${UPLOADS_DIR}" && chown -R www-data:www-data "${UPLOADS_DIR}" mkdir -p "${BIN_UPLOADS_DIR}" && chown -R www-data:www-data "${BIN_UPLOADS_DIR}" +: "${PUBLIC_MAX_UPLOAD_SIZE_MB:=50}" +echo "[*] Rendering nginx config (client_max_body_size = ${PUBLIC_MAX_UPLOAD_SIZE_MB}m)..." +envsubst '${PUBLIC_MAX_UPLOAD_SIZE_MB}' < /etc/nginx/khi.conf.template > /etc/nginx/conf.d/default.conf + # Start supervisord in the background echo "[*] Starting supervisord..." /usr/bin/supervisord -n -c /etc/supervisor/conf.d/supervisord.conf & diff --git a/khi.conf b/khi.conf.template similarity index 94% rename from khi.conf rename to khi.conf.template index 5f80133..b853ff5 100644 --- a/khi.conf +++ b/khi.conf.template @@ -11,6 +11,8 @@ server { listen 80; server_name ctf.hacksu.com dev.ctf.hacksu.com; + client_max_body_size ${PUBLIC_MAX_UPLOAD_SIZE_MB}m; + # block access to all hidden files location ~ /\. { return 404; diff --git a/src/lib/components/file_upload.svelte b/src/lib/components/file_upload.svelte index db04a0f..50e50c5 100644 --- a/src/lib/components/file_upload.svelte +++ b/src/lib/components/file_upload.svelte @@ -8,6 +8,7 @@ import ChevronDown from "@lucide/svelte/icons/chevron-down"; import LoaderCircle from "@lucide/svelte/icons/loader-circle"; import Trash2 from "@lucide/svelte/icons/trash-2"; + import { MAX_UPLOAD_FILE_SIZE, MAX_UPLOAD_SIZE_MB } from "$lib/upload-limits"; let { summaryText, @@ -46,8 +47,6 @@ } function handleFileInput(e: Event) { - const MAX_FILE_SIZE = 12 * 1024 * 1024; // 12 MB - const input = e.target as HTMLInputElement; const picked = Array.from(input.files ?? []); @@ -69,9 +68,9 @@ } } - const tooBig = picked.filter(f => f.size > MAX_FILE_SIZE); + const tooBig = picked.filter(f => f.size > MAX_UPLOAD_FILE_SIZE); if (tooBig.length > 0) { - error = `Files exceed 12 MB limit: ${tooBig.map(f => f.name).join(", ")}`; + error = `Files exceed ${MAX_UPLOAD_SIZE_MB} MB limit: ${tooBig.map(f => f.name).join(", ")}`; if (fileInput) fileInput.value = ""; selectedFiles = []; return; diff --git a/src/lib/upload-limits.ts b/src/lib/upload-limits.ts new file mode 100644 index 0000000..88d7c0e --- /dev/null +++ b/src/lib/upload-limits.ts @@ -0,0 +1,4 @@ +import { env } from "$env/dynamic/public"; + +export const MAX_UPLOAD_SIZE_MB = Number(env.PUBLIC_MAX_UPLOAD_SIZE_MB) || 50; +export const MAX_UPLOAD_FILE_SIZE = MAX_UPLOAD_SIZE_MB * 1024 * 1024; diff --git a/src/routes/admin/+page.server.ts b/src/routes/admin/+page.server.ts index d8ae4f7..78ce473 100644 --- a/src/routes/admin/+page.server.ts +++ b/src/routes/admin/+page.server.ts @@ -21,6 +21,7 @@ import { join, basename } from "path"; import type { ChallengeForm } from "$lib/database/db"; import { SHA256 } from '$lib/utilities'; import { encryptFlag } from '$lib/server/flag-crypto'; +import { MAX_UPLOAD_FILE_SIZE } from '$lib/upload-limits'; const uploadDir = process.env.UPLOADS_DIR ?? join(process.cwd(), "uploads"); const binUploadDir = process.env.BIN_UPLOADS_DIR ?? join(process.cwd(), "ctf"); @@ -239,10 +240,8 @@ export const actions = { }); // check for file sizes and remove large files - const MAX_FILE_SIZE = 12 * 1024 * 1024; // 12 MB - - const largeFiles = files.filter(f => f.size > MAX_FILE_SIZE); - files = files.filter(f => f.size <= MAX_FILE_SIZE); + const largeFiles = files.filter(f => f.size > MAX_UPLOAD_FILE_SIZE); + files = files.filter(f => f.size <= MAX_UPLOAD_FILE_SIZE); if (files.length === 0 || files.every(f => f.size === 0)) { return { @@ -311,10 +310,8 @@ export const actions = { }); // check for file sizes and remove large files - const MAX_FILE_SIZE = 12 * 1024 * 1024; // 12 MB - - const largeFiles = files.filter(f => f.size > MAX_FILE_SIZE); - files = files.filter(f => f.size <= MAX_FILE_SIZE); + const largeFiles = files.filter(f => f.size > MAX_UPLOAD_FILE_SIZE); + files = files.filter(f => f.size <= MAX_UPLOAD_FILE_SIZE); if (files.length === 0 || files.every(f => f.size === 0)) { return { @@ -361,10 +358,8 @@ export const actions = { }); // check for file sizes and remove large files - const MAX_FILE_SIZE = 12 * 1024 * 1024; // 12 MB - - const largeFiles = files.filter(f => f.size > MAX_FILE_SIZE); - files = files.filter(f => f.size <= MAX_FILE_SIZE); + const largeFiles = files.filter(f => f.size > MAX_UPLOAD_FILE_SIZE); + files = files.filter(f => f.size <= MAX_UPLOAD_FILE_SIZE); if (files.length === 0 || files.every(f => f.size === 0)) { return {