feat: Implement service-to-service authentication, centralize environment configuration, and harden Docker security.
This commit is contained in:
@@ -8,7 +8,17 @@ const { Server } = require('socket.io');
|
||||
const app = express();
|
||||
const prisma = new PrismaClient();
|
||||
|
||||
app.use(cors());
|
||||
// SECURITY: Configure CORS with specific origins
|
||||
const ALLOWED_ORIGINS = process.env.ALLOWED_ORIGINS
|
||||
? process.env.ALLOWED_ORIGINS.split(',')
|
||||
: ['http://localhost:5173', 'http://127.0.0.1:5173'];
|
||||
|
||||
app.use(cors({
|
||||
origin: ALLOWED_ORIGINS,
|
||||
credentials: true,
|
||||
methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'],
|
||||
allowedHeaders: ['Content-Type', 'Authorization', 'X-Service-Key']
|
||||
}));
|
||||
app.use(express.json());
|
||||
|
||||
// Rate limiting simple para /api/detect (G)
|
||||
@@ -44,8 +54,9 @@ setInterval(() => {
|
||||
const server = http.createServer(app);
|
||||
const io = new Server(server, {
|
||||
cors: {
|
||||
origin: "*",
|
||||
methods: ["GET", "POST"]
|
||||
origin: ALLOWED_ORIGINS,
|
||||
methods: ["GET", "POST"],
|
||||
credentials: true
|
||||
}
|
||||
});
|
||||
|
||||
@@ -270,8 +281,26 @@ app.post('/api/people/bulk-approve', authenticateToken, isAdmin, async (req, res
|
||||
}
|
||||
});
|
||||
|
||||
// Detection Endpoint (from Python) with Rate Limiting (G)
|
||||
app.post('/api/detect', async (req, res) => {
|
||||
// Detection Endpoint (from Python) with Rate Limiting and Service Auth
|
||||
// SECURITY: Requires X-Service-Key header for service-to-service auth
|
||||
const validateServiceKey = (req, res, next) => {
|
||||
const serviceKey = process.env.SERVICE_API_KEY;
|
||||
|
||||
// If no key configured, allow (development mode) but warn
|
||||
if (!serviceKey) {
|
||||
console.warn('⚠️ SERVICE_API_KEY not configured - /api/detect is unprotected!');
|
||||
return next();
|
||||
}
|
||||
|
||||
const providedKey = req.headers['x-service-key'];
|
||||
if (providedKey !== serviceKey) {
|
||||
console.warn(`🔒 Rejected /api/detect request - invalid service key from ${req.ip}`);
|
||||
return res.status(401).json({ error: 'Invalid service key' });
|
||||
}
|
||||
next();
|
||||
};
|
||||
|
||||
app.post('/api/detect', validateServiceKey, async (req, res) => {
|
||||
const clientIp = req.ip || req.connection.remoteAddress;
|
||||
|
||||
// Check rate limit
|
||||
@@ -368,7 +397,18 @@ server.listen(PORT, async () => {
|
||||
const userCount = await prisma.user.count();
|
||||
if (userCount === 0) {
|
||||
console.log('No users found. Creating default admin user...');
|
||||
const hashedPassword = await bcrypt.hash('admin123', 10);
|
||||
|
||||
// SECURITY: Use env var or generate random password
|
||||
let adminPassword = process.env.ADMIN_PASSWORD;
|
||||
let isGenerated = false;
|
||||
|
||||
if (!adminPassword) {
|
||||
// Generate a secure random password
|
||||
adminPassword = require('crypto').randomBytes(12).toString('base64url');
|
||||
isGenerated = true;
|
||||
}
|
||||
|
||||
const hashedPassword = await bcrypt.hash(adminPassword, 10);
|
||||
await prisma.user.create({
|
||||
data: {
|
||||
username: 'admin',
|
||||
@@ -376,7 +416,18 @@ server.listen(PORT, async () => {
|
||||
role: 'ADMIN'
|
||||
}
|
||||
});
|
||||
console.log('Default admin created: admin / admin123');
|
||||
|
||||
console.log('═'.repeat(50));
|
||||
console.log('🔐 ADMIN USER CREATED');
|
||||
console.log(' Username: admin');
|
||||
if (isGenerated) {
|
||||
console.log(` Password: ${adminPassword}`);
|
||||
console.log(' ⚠️ SAVE THIS PASSWORD - it won\'t be shown again!');
|
||||
console.log(' 💡 Set ADMIN_PASSWORD env var to use a custom password');
|
||||
} else {
|
||||
console.log(' Password: [from ADMIN_PASSWORD env var]');
|
||||
}
|
||||
console.log('═'.repeat(50));
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Error seeding admin user:', err);
|
||||
|
||||
@@ -1,6 +1,12 @@
|
||||
const jwt = require('jsonwebtoken');
|
||||
|
||||
const JWT_SECRET = process.env.JWT_SECRET || 'your-secret-key-change-this';
|
||||
// SECURITY: JWT_SECRET must be configured via environment variable
|
||||
const JWT_SECRET = process.env.JWT_SECRET;
|
||||
if (!JWT_SECRET) {
|
||||
console.error('❌ FATAL: JWT_SECRET environment variable is required');
|
||||
console.error(' Generate one with: node -e "console.log(require(\'crypto\').randomBytes(32).toString(\'hex\'))"');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const authenticateToken = (req, res, next) => {
|
||||
const authHeader = req.headers['authorization'];
|
||||
|
||||
Reference in New Issue
Block a user