diff --git a/backend/compose.yaml b/backend/compose.yaml index ffb3ec8..a9253e8 100644 --- a/backend/compose.yaml +++ b/backend/compose.yaml @@ -24,8 +24,8 @@ services: docker-socket-proxy: container_name: dockerproxy # Uncomment the following ports section for development - # ports: - # - "2375:2375" + ports: + - "2375:2375" environment: - CONTAINERS=1 # create/start/stop/inspect containers - POST=1 # CRITICAL: allow POST requests @@ -57,6 +57,8 @@ services: restart: unless-stopped ports: - "${PORT:-3000}:3000" + volumes: + - ./src/utils/ca.pem:/app/dist/utils/key.pem:ro env_file: - .env networks: diff --git a/backend/dockerfile b/backend/dockerfile index ec5a888..124e0a3 100644 --- a/backend/dockerfile +++ b/backend/dockerfile @@ -37,6 +37,9 @@ RUN npm ci --omit=dev && \ # Copy built files from builder stage COPY --from=builder /app/dist ./dist + +COPY src/utils/ca.pem src/utils/cert.pem ./dist/utils/ + # Change ownership to non-root user RUN chown -R appuser:appgroup /app diff --git a/backend/nginx.conf b/backend/nginx.conf new file mode 100644 index 0000000..dd12cd3 --- /dev/null +++ b/backend/nginx.conf @@ -0,0 +1,25 @@ +events { + # Configuration for connection processing + worker_connections 1024; +} +http { + include mime.types; + + upstream backendserver { + server 127.0.0.1:3000; + } + + server { + listen 80; # Listen on standard web port + server_name localhost; + + location / { + proxy_pass http://backendserver/; + # Important headers to keep the user's info intact + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } + } +} diff --git a/backend/scripts/ctfImagePrep.sh b/backend/scripts/ctfImagePrep.sh new file mode 100755 index 0000000..6331a13 --- /dev/null +++ b/backend/scripts/ctfImagePrep.sh @@ -0,0 +1,97 @@ +## this script is specific for our use case in future will be updated to be more dynamic +#!/bin/sh + +command_exists() { + command -v "$1" >/dev/null 2>&1 +} +install_package() { + local package=$1 + if command_exists apt; then + sudo apt install -y "$package" + elif command_exists pacman; then + sudo pacman -S --noconfirm "$package" + elif command_exists dnf; then + sudo dnf install -y "$package" + elif command_exists yum; then + sudo yum install -y "$package" + elif command_exists brew; then + brew install "$package" + else + echo "❌ Unsupported package manager. Please install $package manually." + fi +} +echo "checking prequisite" + +#check docker +if ! command -v docker >/dev/null 2>&1 +then + echo "docker not found installing it" + install_package docker + sudo systemctl start docker + sudo usermod -aG docker $USER +fi +# check git +if ! command -v git >/dev/null 2>&1 +then + echo "Git not found installing it" + install_package git +fi + +echo "Passed, checking does the Ctf challenge images/network exsits" +TMP_DIR_PATH="$HOME/ctf_prep_tmp" +BACKEND_IMAGE_NAME='ctf_ssrf_race_backend' +FRONTEND_IMAGE_NAME='ctf_ssrf_race_frontend' +SSRF_GITHUB_URL="https://github.com/sfeedbackx/ssrf-race.git" + +if docker network ls | grep -q -w 'ctf_ssrf_race' ; then + echo 'network exsist' +else + echo 'creating network'; + docker network create ctf_ssrf_race +fi +docker image ls | grep -q -w $FRONTEND_IMAGE_NAME +FRONTEND_IMAGE_FOUND=$? +docker image ls | grep -q -w $BACKEND_IMAGE_NAME +BACKEND_IMAGE_FOUND=$? + +if [ "$FRONTEND_IMAGE_FOUND" -ne 0 ] || [ "$BACKEND_IMAGE_FOUND" -ne 0 ]; then + echo "frontent or backend image missing" + ## first check if folder exsits + if [ ! -d "$TMP_DIR_PATH" ]; then + echo "creating tmp dir" + mkdir $TMP_DIR_PATH + else + echo "tmp folder exsits , preforming cleansing" + find $TMP_DIR_PATH -mindepth 1 -exec rm -rf {} + + fi + ## preforming clone of ssrf challenge to tmp dir + echo "cloning ssrf_challenge" + git clone $SSRF_GITHUB_URL $TMP_DIR_PATH +if $BACKEND_IMAGE_FOUND; then + echo 'backend image exsist' +else + ## we will be putting thes github repo of ctf challenge in folder called ctf_prep_tmp + echo 'creating backend image'; + ## now we go and build the image of the backend + cd $TMP_DIR_PATH/backend + echo "building backend image" + docker build -t $BACKEND_IMAGE_NAME . + echo "finished building backend image" +fi +if $FRONTEND_IMAGE_FOUND; then + echo 'backend image exsist' +else + echo 'creating frontend image'; + ## now we go and build the image of the frontend + cd $TMP_DIR_PATH/frontend + echo "building frontend image" + docker build -t $FRONTEND_IMAGE_NAME . + echo "finished building frontend image" +fi +else + echo "image are well defined , removing tmp folder" + rm -rf $TMP_DIR_PATH +fi + + + diff --git a/backend/scripts/dockerTlsSetup.sh b/backend/scripts/dockerTlsSetup.sh new file mode 100755 index 0000000..fd150a3 --- /dev/null +++ b/backend/scripts/dockerTlsSetup.sh @@ -0,0 +1,147 @@ +#!/bin/sh + +command_exists() { + command -v "$1" >/dev/null 2>&1 +} + +install_package() { + local package=$1 + if command_exists apt; then + sudo apt install -y "$package" + elif command_exists pacman; then + sudo pacman -S --noconfirm "$package" + elif command_exists dnf; then + sudo dnf install -y "$package" + elif command_exists brew; then + brew install "$package" + else + echo "❌ Unsupported package manager. Please install $package manually." + fi +} + +if [ $# -ne 2 ] ; then + echo "usage $0 CA-IP EXT-IP" + exit 1 +fi + +CA_IP=$1 +EXT_IP=$2 + +echo "checking prerequisite" + +#check docker +if ! command -v docker >/dev/null 2>&1 +then + echo "docker not found installing it" + install_package docker + sudo systemctl start docker + sudo usermod -aG docker $USER + +fi + +#check openssl +if ! command -v openssl >/dev/null 2>&1 +then + echo "openssl not found plz installing it" + install_package openssl +fi + +echo "starting setup the certificate" + +CERT_PATH="$HOME/docker-certs" +if [ ! -d "$CERT_PATH" ]; then + echo "creating certif dir" + mkdir -p "$CERT_PATH" +else + echo "certif folder exists, performing cleansing" + find "$CERT_PATH" -mindepth 1 -exec rm -rf {} + +fi + +cd "$CERT_PATH" + +echo "generating CA key" +openssl genrsa -out ca-key.pem 4096 + +echo "generating CA certificate" +openssl req -new -x509 -days 365 -key ca-key.pem -sha256 -out ca.pem -subj "/CN=docker-ca" + +echo "generating Server certificate" +echo "Create Server Private Key" +openssl genrsa -out server-key.pem 4096 + +echo "Create Certificate Signing Request" +openssl req -subj "/CN=$CA_IP" -sha256 -new -key server-key.pem -out server.csr + +echo "Configure Certificate Extensions" +echo "subjectAltName = IP:$EXT_IP,IP:127.0.0.1" >> extfile.cnf +echo "extendedKeyUsage = serverAuth" >> extfile.cnf + +echo "Sign the Server Certificate" +openssl x509 -req -days 365 -sha256 -in server.csr -CA ca.pem -CAkey ca-key.pem \ + -CAcreateserial -out server-cert.pem -extfile extfile.cnf + +echo "Generate Client Certificates" +echo "Create Client Private Key" +openssl genrsa -out key.pem 4096 + +echo "Create Client CSR" +openssl req -subj '/CN=client' -new -key key.pem -out client.csr + +echo "Configure Client Extensions" +echo "extendedKeyUsage = clientAuth" > extfile-client.cnf + +echo "Sign the Client Certificate" +openssl x509 -req -days 365 -sha256 -in client.csr -CA ca.pem -CAkey ca-key.pem \ + -CAcreateserial -out cert.pem -extfile extfile-client.cnf + +echo "Set Proper Permissions" +chmod 0400 ca-key.pem key.pem server-key.pem +chmod 0444 ca.pem server-cert.pem cert.pem + +echo "Clean Up Temporary Files" +rm -v client.csr server.csr extfile.cnf extfile-client.cnf + +DOCKER_PATH=/etc/docker/certs + +echo "Configure Docker Daemon" +if [ ! -d "$DOCKER_PATH" ]; then + echo "creating docker remote dir" + sudo mkdir -p "$DOCKER_PATH" +else + echo "Docker remote folder exists, performing cleansing" + sudo find "$DOCKER_PATH" -mindepth 1 -exec rm -rf {} + +fi + +sudo cp "$CERT_PATH/ca.pem" "$DOCKER_PATH" +sudo cp "$CERT_PATH/server-cert.pem" "$DOCKER_PATH" +sudo cp "$CERT_PATH/server-key.pem" "$DOCKER_PATH" + +echo "Create Docker Service Override" +DOCKER_OVERRIDE_PATH="/etc/systemd/system/docker.service.d" +if [ ! -d "$DOCKER_OVERRIDE_PATH" ]; then + echo "creating docker override dir" + sudo mkdir -p "$DOCKER_OVERRIDE_PATH" +else + echo "Docker override folder exists, performing cleansing" + sudo find "$DOCKER_OVERRIDE_PATH" -mindepth 1 -exec rm -rf {} + +fi + +sudo tee "$DOCKER_OVERRIDE_PATH/override.conf" > /dev/null << 'EOF' +[Service] +ExecStart= +ExecStart=/usr/bin/dockerd -H fd:// -H tcp://0.0.0.0:2376 \ + --tlsverify \ + --tlscacert=/etc/docker/certs/ca.pem \ + --tlscert=/etc/docker/certs/server-cert.pem \ + --tlskey=/etc/docker/certs/server-key.pem +EOF + +echo "Reload and Restart Docker" +sudo systemctl daemon-reload +sudo systemctl restart docker + +echo "Please copy the ca.pem, key.pem and cert.pem to the client to use it thanks" +sleep 5 + +echo "printing docker status" +sudo systemctl status docker diff --git a/backend/scripts/migrateScript.ts b/backend/scripts/migrateScript.ts index 4e90db7..fd46f76 100644 --- a/backend/scripts/migrateScript.ts +++ b/backend/scripts/migrateScript.ts @@ -51,19 +51,44 @@ const containersConfig: containerConfig[] = [config0, config1]; const ctfChallenges: Ictf[] = []; -ctfChallenges.push({ - name: 'SSRF-RACE', - withSite: true, - containersConfig: containersConfig, - description: 'Who said education has to be expensive? This platform seems to handle enrollment and pricing automatically. Still, nothing is ever as perfect as it looks.', - difficulty: ctfDifficulty.MID, - flag: 'cll{ss098fud63c2xgXPuVPimY3ZmkDmFsI+RfhCdqccOZwJKBqQI=}', - type: 'WEB_EXPLOIT', - resources: [], - hints: ['Hint 1: The server talks to itself. What happens when you make it ask the right questions?', - 'Hint 2: Speed beats logic. Sometimes clicking faster than the server can think pays off.' - ], -}); +ctfChallenges.push( + { + name: 'SSRF-RACE', + withSite: true, + containersConfig: containersConfig, + description: 'Who said education has to be expensive? This platform seems to handle enrollment and pricing automatically. Still, nothing is ever as perfect as it looks.', + difficulty: ctfDifficulty.MID, + flag: 'cll{ss098fud63c2xgXPuVPimY3ZmkDmFsI+RfhCdqccOZwJKBqQI=}', + type: 'WEB_EXPLOIT', + resources: [], + hints: ['Hint 1: The server talks to itself. What happens when you make it ask the right questions?', + 'Hint 2: Speed beats logic. Sometimes clicking faster than the server can think pays off.' + ], + }, + { + name: 'Steganography_basics', + withSite: false, + description: 'Do you know what is steganography ?', + difficulty: ctfDifficulty.EASY, + flag: 'cll{Fqj1Lkirsyi2sLn0O7aX1NIoYZggKuvy8MEfx1sU/PI=}', + type: 'FORENSICS', + resources: ['127.0.0.1:6650'], + hints: [], + }, + { + name: 'STEGANOGRAPHY2', + withSite: false, + description: "So you know Steganography basics but didn't we deserve 20 ?", + difficulty: ctfDifficulty.MID, + flag: 'cll{iVwbTtAoYXciA486sEXwGMT1geaJOym3pIsEGeQQQNg=}', + type: 'FORENSICS', + resources: ['127.0.0.1:6600'], + hints: [ + 'Hint 1 : i can see the password there ! ', + 'Hint 2 : really! we deserve 20' + ], + } +); const cleanCtfCollection = async () => { console.log('deleting ctf collection...'); diff --git a/backend/src/models/ctfModel.ts b/backend/src/models/ctfModel.ts index e34bd4f..c12280c 100644 --- a/backend/src/models/ctfModel.ts +++ b/backend/src/models/ctfModel.ts @@ -14,7 +14,7 @@ const ctfSchema = new Schema( type: String, trim: true, required: [true, 'type required'], - enum: ['WEB_EXPLOIT', 'BE', 'OTHER'], + enum: ['WEB_EXPLOIT', 'BE', 'OTHER', 'FORENSICS'], }, withSite: { @@ -31,7 +31,7 @@ const ctfSchema = new Schema( type: String, trim: true, required: [true, 'difficulty required'], - enum: ['ESAY', 'MID', 'HARD'], + enum: ['EASY', 'MID', 'HARD'], }, hints: [ { diff --git a/backend/src/types/ctfTypes.ts b/backend/src/types/ctfTypes.ts index 630b8f3..1ec7d23 100644 --- a/backend/src/types/ctfTypes.ts +++ b/backend/src/types/ctfTypes.ts @@ -24,7 +24,7 @@ interface IMongoDocument extends ITimestamps { export interface Ictf { name: string; description?: string | undefined; - type: 'WEB_EXPLOIT' | 'BE' | 'OTHER'; + type: 'WEB_EXPLOIT' | 'BE' | 'OTHER' | 'FORENSICS'; resources: string[]; withSite: boolean; difficulty: ctfDifficulty;