mcp-hub-007: Auth hardening — per-service secrets and env-based config

This commit is contained in:
Agent 2026-03-13 10:17:45 +00:00
parent 91f0ce271b
commit 85b3f5b6e2
4 changed files with 36 additions and 6 deletions

View file

@ -4,7 +4,7 @@ module.exports = {
name: 'mcp-hub',
script: 'src/index.js',
cwd: '/workspace',
env: { NODE_ENV: 'production', PORT: 3000 },
env: { NODE_ENV: 'development', PORT: 3000, HUB_AUTH: JSON.stringify({"sample-mcp": "changeme"}) },
max_restarts: 10,
restart_delay: 1000,
log_date_format: 'YYYY-MM-DD HH:mm:ss Z',
@ -14,7 +14,7 @@ module.exports = {
name: 'sample-mcp',
script: 'sample-mcp/index.js',
cwd: '/workspace',
env: { NODE_ENV: 'production' },
env: { NODE_ENV: 'development', MCP_SECRET: 'changeme' },
max_restarts: 10,
restart_delay: 2000,
log_date_format: 'YYYY-MM-DD HH:mm:ss Z',

View file

@ -5,7 +5,7 @@ const WebSocket = require('ws');
const HUB_URL = process.env.HUB_URL || 'wss://mcp.arik.work/ws/register';
const HUB_URL_FALLBACK = 'ws://mcp.arik.work/ws/register';
const SERVICE_ID = 'sample-mcp';
const SECRET = 'dev-secret';
const SECRET = process.env.MCP_SECRET || 'dev-secret';
let reconnectDelay = 1000;
let ws = null;

View file

@ -1,4 +1,27 @@
const DEV_SECRET = 'dev-secret';
let serviceAuthMap = null;
if (process.env.HUB_AUTH) {
try {
serviceAuthMap = JSON.parse(process.env.HUB_AUTH);
} catch (e) {
console.error('[config] Failed to parse HUB_AUTH JSON:', e.message);
process.exit(1);
}
} else if (process.env.NODE_ENV === 'production') {
console.error('[config] HUB_AUTH must be set in production');
process.exit(1);
}
function getServiceSecret(serviceId) {
if (serviceAuthMap) {
return serviceAuthMap[serviceId] !== undefined ? serviceAuthMap[serviceId] : null;
}
// Dev fallback: accept dev-secret for any service
return DEV_SECRET;
}
module.exports = {
PORT: parseInt(process.env.PORT, 10) || 3000,
HUB_SECRET: process.env.HUB_SECRET || 'dev-secret',
getServiceSecret,
};

View file

@ -19,7 +19,7 @@ function setupWsServer(httpServer) {
});
});
wss.on('connection', (ws) => {
wss.on('connection', (ws, req) => {
let serviceId = null;
let authenticated = false;
let missedPongs = 0;
@ -34,7 +34,14 @@ function setupWsServer(httpServer) {
return;
}
if (msg.type !== 'register' || !msg.serviceId || msg.secret !== config.HUB_SECRET) {
if (msg.type !== 'register' || !msg.serviceId) {
ws.close(4001, 'unauthorized');
return;
}
const expectedSecret = config.getServiceSecret(msg.serviceId);
if (expectedSecret === null || msg.secret !== expectedSecret) {
console.log(`[ws] auth failed for serviceId=${msg.serviceId} from ${req.socket.remoteAddress}`);
ws.close(4001, 'unauthorized');
return;
}