EverydayTech Platform - Developer Reference
Complete Source Code Documentation - All Applications
Loading...
Searching...
No Matches
MeshCentral Secure Proxy Architecture

Security Improvement

Problem: Direct iframe embedding of MeshCentral exposes:

  • MeshCentral server URL to client browser
  • Requires separate authentication or exposed credentials
  • Vulnerable to credential leakage

Solution: Backend proxy approach:

  • Dashboard authenticates to backend only (existing JWT)
  • Backend manages MeshCentral sessions server-side
  • MeshCentral credentials never exposed to client
  • All requests proxied through backend API

Architecture

┌─────────────┐ ┌─────────────┐ ┌──────────────┐
│ Dashboard │ JWT Auth │ Backend │ Credentials │ MeshCentral │
│ (Client) │ ────────────▶ │ Proxy │ ─────────────▶ │ Server │
│ │ │ │ │ │
│ iframe │ ◀──────────── │ Session │ ◀─────────── │ Devices │
│ │ Proxy URL │ Manager │ API Calls │ │
└─────────────┘ └─────────────┘ └──────────────┘
▲ │ │
│ │ │
└──────────────────────────────┴───────────────────────────────┘
All traffic through backend proxy
MeshCentral URL never exposed to client

API Endpoints

1. Create Secure Session

POST /api/meshcentral/session/:agentId

Creates a secure session token for accessing a specific agent's MeshCentral features.

Authentication: Bearer JWT token (existing dashboard auth)

Request Body:

{
"viewMode": 11 // Optional: 10=General, 11=Desktop, 12=Terminal, etc.
}

Response:

{
"success": true,
"sessionToken": "8f3e9c2a1b4d...",
"expiresAt": "2025-01-15T18:30:00Z",
"agent": {
"id": 115,
"uuid": "2c3e2d7a-c521-4844-b44b-a30b4c2ec4d8",
"hostname": "testing",
"platform": "linux",
"nodeId": "sqtYR0SN2DGaJsuSQZsNWxu0H643deE8$Jq9Ymm@UBywhJwXh2sj8ArX4KPoyZK4"
},
"proxyUrl": "/api/meshcentral/proxy/8f3e9c2a1b4d...",
"viewMode": 11
}

Session Expiry: 4 hours (configurable)

Database Storage:

-- Sessions stored with:
CREATE TABLE meshcentral_sessions (
session_token VARCHAR(64) UNIQUE, -- Random secure token
user_id INT, -- Dashboard user ID
agent_id INT, -- Target agent ID
node_id VARCHAR(255), -- MeshCentral node ID
view_mode INT, -- Remote feature (10-19)
meshcentral_cookie TEXT, -- Server-side MeshCentral auth
expires_at TIMESTAMP, -- Auto-expire old sessions
created_at TIMESTAMP,
last_used_at TIMESTAMP
);

2. Proxy MeshCentral Requests

ALL /api/meshcentral/proxy/:sessionToken/*

Proxies all HTTP requests to MeshCentral using server-side credentials.

Authentication: Session token (from step 1)

Flow:

  1. Client makes request to proxy endpoint
  2. Backend validates session token
  3. Backend authenticates to MeshCentral using server-side credentials
  4. Backend forwards request to MeshCentral
  5. Backend returns response to client

Example:

// Dashboard embeds iframe:
<iframe src="/api/meshcentral/proxy/8f3e9c2a1b4d..." />
// Backend proxies to:
https://rmm-psa-meshcentral-aq48h.ondigitalocean.app/...
// (URL never exposed to client)

Dashboard Integration

Updated MeshCentralEmbed Component

import { useState, useEffect } from 'react';
import api from '../../utils/api';
import './MeshCentralEmbed.css';
export default function MeshCentralEmbed({ agentId, viewMode = 10, title }) {
const [session, setSession] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
createSecureSession();
}, [agentId, viewMode]);
const createSecureSession = async () => {
try {
setLoading(true);
setError(null);
// Request secure session from backend
const response = await api.post(
`/meshcentral/session/${agentId}`,
{ viewMode }
);
setSession(response.data);
setLoading(false);
} catch (err) {
console.error('Failed to create MeshCentral session:', err);
setError(err.response?.data?.message || 'Failed to connect');
setLoading(false);
}
};
if (loading) {
return (
<div className="meshcentral-loading">
<div className="spinner"></div>
<p>Connecting to {title || 'device'}...</p>
</div>
);
}
if (error) {
return (
<div className="meshcentral-error">
<p>❌ {error}</p>
<button onClick={createSecureSession}>Retry</button>
</div>
);
}
return (
<div className="meshcentral-embed-container">
<div className="meshcentral-header">
<h3>{title || 'MeshCentral'}</h3>
<span className="session-info">
Session expires: {new Date(session.expiresAt).toLocaleTimeString()}
</span>
</div>
<iframe
src={session.proxyUrl}
className="meshcentral-iframe"
sandbox="allow-same-origin allow-scripts allow-forms allow-downloads"
title={title || 'MeshCentral'}
/>
</div>
);
}
// View mode constants
export const VIEWMODE = {
GENERAL: 10, // General device info
DESKTOP: 11, // Remote desktop
TERMINAL: 12, // Terminal/command prompt
FILES: 13, // File manager
INTEL_AMT: 14, // Intel AMT features
CONSOLE: 15, // Device console
EVENTS: 16, // Event log
DETAILS: 17, // Detailed hardware info
PLUGINS: 19 // Plugin interface
};

Security Benefits

✅ Credentials Never Exposed

  • MeshCentral URL only known by backend
  • API credentials stored in server-side .env
  • No credentials in client-side code or network requests

✅ Session Management

  • Temporary session tokens (4-hour expiry)
  • Tokens tied to specific user + agent combination
  • Automatic cleanup of expired sessions

✅ Audit Trail

  • All access logged in meshcentral_sessions table
  • Track which users accessed which agents
  • Monitor session usage and patterns

✅ Access Control

  • Uses existing JWT authentication
  • Respects tenant isolation
  • Requires technician role

✅ CORS & CSP Friendly

  • No cross-origin issues (same domain)
  • No Content-Security-Policy violations
  • Works with strict security headers

Migration from Direct Embedding

Before (Insecure):

// ❌ Exposes MeshCentral URL
const meshUrl = `https://rmm-psa-meshcentral-aq48h.ondigitalocean.app/login?gotonode=${nodeId}`;
<iframe src={meshUrl} />

After (Secure):

// ✅ Backend proxy - credentials never exposed
<MeshCentralEmbed agentId={agentId} viewMode={VIEWMODE.DESKTOP} />

Implementation Checklist

  • Create meshcentral_sessions database table
  • Add POST /api/meshcentral/session/:agentId endpoint
  • Add ALL /api/meshcentral/proxy/:sessionToken/* endpoint
  • Implement full HTTP/WebSocket proxy forwarding
  • Test remote desktop through proxy
  • Test terminal through proxy
  • Test file manager through proxy
  • Deploy to production

Testing

1. Verify Credentials Not Exposed

# Open browser DevTools Network tab
# Access Remote Desktop in dashboard
# Check all requests:
# ❌ Should NOT see: rmm-psa-meshcentral-aq48h.ondigitalocean.app
# ✅ Should see: rmm-psa-backend-t9f7k.ondigitalocean.app

2. Test Session Expiry

# Create session, note expiry time
# Wait for expiry
# Try to use expired session token
# Should get 401 Unauthorized

3. Test Access Control

# User A creates session for Agent 115
# User B tries to use User A's session token
# Should be rejected (different user_id)

Performance Considerations

Session Pooling

Backend maintains single MeshCentral connection per tenant:

// Reuse MeshCentral API connections
const meshAPIPool = new Map(); // tenantId -> MeshCentralAPI instance

WebSocket Proxying

For real-time features (Desktop, Terminal), use WebSocket proxy:

const WebSocket = require('ws');
// Proxy WebSocket connections
wss.on('connection', (clientWs, req) => {
const serverWs = new WebSocket(MESHCENTRAL_WS_URL);
clientWs.on('message', (data) => serverWs.send(data));
serverWs.on('message', (data) => clientWs.send(data));
});

Caching

Cache MeshCentral API responses for 30 seconds:

const apiCache = new Map(); // endpoint -> { data, timestamp }

Environment Variables

# Backend .env
MESHCENTRAL_URL=https://rmm-psa-meshcentral-aq48h.ondigitalocean.app
MESHCENTRAL_USERNAME=admin
MESHCENTRAL_PASSWORD=<secure-password>
MESHCENTRAL_SESSION_EXPIRY=14400 # 4 hours in seconds

References

  • MeshCentral API Documentation
  • /rmm-psa-backend/lib/meshcentral-api.js - API client
  • /rmm-psa-backend/routes/meshcentral.js - Proxy endpoints
  • /rmm-psa-dashboard/MESHCENTRAL_EMBED_GUIDE.md - Original embedding guide