Skip to content
Open
Changes from all commits
Commits
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
73 changes: 48 additions & 25 deletions create-a-container/server.js
Original file line number Diff line number Diff line change
@@ -1,29 +1,47 @@
require('dotenv').config();

const express = require('express');
const bodyParser = require('body-parser');
const session = require('express-session');
const { spawn, exec } = require('child_process');
const path = require('path');
const crypto = require('crypto');
const fs = require('fs'); // Added fs module
const fs = require('fs');
const rateLimit = require('express-rate-limit'); // <-- ADDED

const app = express();
app.use(express.json());

app.set('trust proxy', 1);

// A simple in-memory object to store job status and output
const jobs = {};

// --- Middleware Setup ---
app.use(bodyParser.urlencoded({ extended: true }));
app.use(express.json());
app.use(express.static('public'));
if (!process.env.SESSION_SECRET) {
throw new Error("SESSION_SECRET is not set in environment!");
}

app.use(session({
secret: 'A7d#9Lm!qW2z%Xf8@Rj3&bK6^Yp$0Nc',
resave: false,
saveUninitialized: true,
cookie: { secure: false } // Set to true if using HTTPS
secret: process.env.SESSION_SECRET,
resave: false,
saveUninitialized: true,
cookie: { secure: true }
}));

Check failure

Code scanning / CodeQL

Missing CSRF middleware High

This cookie middleware is serving a
request handler
without CSRF protection.
This cookie middleware is serving a
request handler
without CSRF protection.

app.use(express.static('public'));

// --- Rate Limiter for Login ---
const loginLimiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 5, // limit each IP to 5 requests per windowMs
message: { error: "Too many login attempts. Please try again later." }
});

// --- Route Handlers ---

const PORT = 3000;
app.listen(PORT, () => console.log(`Server running on http://localhost:${PORT}`));

// Serves the main container creation form, protected by login
app.get('/form.html', (req, res) => {
if (!req.session.user) {
Expand All @@ -32,17 +50,30 @@
res.sendFile(path.join(__dirname, 'views', 'form.html'));
});

// Handles user login
app.post('/login', (req, res) => {
// Handles user login with rate limiting
app.post('/login', loginLimiter, (req, res) => {
const { username, password } = req.body;

exec(`node /root/bin/js/runner.js authenticateUser ${username} ${password}`, (err, stdout) => {
if (err) {
console.error("Login script execution error:", err);
const runner = spawn('node', ['/root/bin/js/runner.js', 'authenticateUser', username, password]);

let stdoutData = '';
let stderrData = '';

runner.stdout.on('data', (data) => {
stdoutData += data.toString();
});

runner.stderr.on('data', (data) => {
stderrData += data.toString();
});

runner.on('close', (code) => {
if (code !== 0) {
console.error("Login script execution error:", stderrData);
return res.status(500).json({ error: "Server error during authentication." });
}

if (stdout.trim() === 'true') {
if (stdoutData.trim() === 'true') {
req.session.user = username;
req.session.proxmoxUsername = username;
req.session.proxmoxPassword = password;
Expand All @@ -53,15 +84,12 @@
});
});

// ✨ UPDATED: API endpoint to get user's containers
// API endpoint to get user's containers
app.get('/api/my-containers', (req, res) => {
if (!req.session.user) {
return res.status(401).json({ error: "Unauthorized" });
}
// The username in port_map.json doesn't have the @pve suffix
const username = req.session.user.split('@')[0];

// Command to read the remote JSON file
const command = "ssh [email protected] 'cat /etc/nginx/port_map.json'";

exec(command, (err, stdout, stderr) => {
Expand All @@ -72,7 +100,6 @@
try {
const portMap = JSON.parse(stdout);
const userContainers = Object.entries(portMap)
// This check now ensures 'details' exists and has a 'user' property before comparing
.filter(([_, details]) => details && details.user === username)
.map(([name, details]) => ({ name, ...details }));

Expand Down Expand Up @@ -155,8 +182,4 @@
clearInterval(intervalId);
res.end();
});
});

// --- Server Initialization ---
const PORT = 3000;
app.listen(PORT, () => console.log(`Server running on http://localhost:${PORT}`));
});