diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..ef8bc48
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,40 @@
+# .gitignore
+
+# Build artifacts
+src/build/
+src/bin/
+*.o
+*.so
+*.a
+
+# IDE files
+.vscode/
+.idea/
+*.swp
+*.swo
+*~
+
+# OS files
+.DS_Store
+Thumbs.db
+
+# Logs
+*.log
+logs/
+
+# Environment files
+.env
+.env.local
+
+# Custom values (may contain sensitive info)
+my-values.yaml
+*-values.yaml
+!values.yaml
+
+# Test files
+test/
+*.test
+
+# Temporary files
+tmp/
+temp/
diff --git a/web-gateway/.env.example b/web-gateway/.env.example
index 2390c67..a39b170 100644
--- a/web-gateway/.env.example
+++ b/web-gateway/.env.example
@@ -3,14 +3,13 @@
# Server
PORT=8080
NODE_ENV=production
-LOG_LEVEL=info
# RDP Broker Connection
RDP_BROKER_HOST=rdpbroker
RDP_BROKER_PORT=3389
-# Session Configuration
-SESSION_TIMEOUT=3600000
-
-# Security (set these in production)
-# SESSION_SECRET=your-secret-key-here
+# Optional: Pre-configure RDP Targets
+# Format: JSON array of target objects
+# If not set, RdpBroker will provide targets dynamically
+# Example:
+# RDP_TARGETS=[{"name":"Server1","host":"srv1.example.com","port":3389,"description":"Production Server"},{"name":"Server2","host":"srv2.example.com","port":3389,"description":"Development Server"}]
diff --git a/web-gateway/Dockerfile b/web-gateway/Dockerfile
index cf44eb8..1638d3a 100644
--- a/web-gateway/Dockerfile
+++ b/web-gateway/Dockerfile
@@ -7,7 +7,8 @@ WORKDIR /app
COPY package*.json ./
# Install dependencies
-RUN npm ci --only=production
+RUN npm install \
+ && npm ci --only=production
# Production stage
FROM node:18-alpine
@@ -19,10 +20,6 @@ RUN apk add --no-cache dumb-init
RUN addgroup -g 1001 -S nodejs && \
adduser -S nodejs -u 1001
-# Create necessary directories
-RUN mkdir -p /var/log/rdp-web-gateway && \
- chown -R nodejs:nodejs /var/log/rdp-web-gateway
-
WORKDIR /app
# Copy dependencies from builder
diff --git a/web-gateway/README.md b/web-gateway/README.md
index 39164e9..77df151 100644
--- a/web-gateway/README.md
+++ b/web-gateway/README.md
@@ -7,9 +7,11 @@ HTML5 WebSocket-based gateway for accessing RDP connections through a web browse
- 🌐 **Browser-Based Access** - Connect to RDP sessions from any modern web browser
- 🔒 **Secure WebSocket** - Real-time bidirectional communication
- 🎨 **Modern UI** - Clean, responsive interface
-- 🔑 **Session Management** - Automatic session cleanup and timeout
-- 📊 **Activity Monitoring** - Track active connections
+- 🔑 **Simplified Authentication** - Credentials passed directly to RdpBroker
+- 📊 **Service Health Monitoring** - Automatic RdpBroker availability checks
+- 🎯 **Dynamic Target Loading** - Targets fetched from configuration or RdpBroker
- ⚡ **Low Latency** - Optimized for performance
+- ☁️ **Kubernetes Native** - Console-only logging for cloud environments
## Architecture
@@ -63,10 +65,23 @@ Edit `.env` file:
PORT=8080
RDP_BROKER_HOST=rdpbroker
RDP_BROKER_PORT=3389
-LOG_LEVEL=info
-SESSION_TIMEOUT=3600000
+NODE_ENV=production
+
+# Optional: Pre-configure RDP targets (JSON array)
+# If not set, RdpBroker will provide targets dynamically
+RDP_TARGETS=[{"name":"Server1","host":"srv1.example.com","port":3389,"description":"Production Server"}]
```
+### Environment Variables
+
+| Variable | Description | Default |
+|----------|-------------|---------|
+| `PORT` | Web server listening port | `8080` |
+| `RDP_BROKER_HOST` | RdpBroker hostname | `rdpbroker` |
+| `RDP_BROKER_PORT` | RdpBroker port | `3389` |
+| `RDP_TARGETS` | JSON array of pre-configured targets | `null` |
+| `NODE_ENV` | Environment mode | `development` |
+
## Usage
### Access the Web Interface
@@ -78,43 +93,55 @@ SESSION_TIMEOUT=3600000
### API Endpoints
-#### POST /api/auth/login
-Authenticate user and create session.
-
-```json
-{
- "username": "user@domain.com",
- "password": "password"
-}
-```
+#### GET /health
+Health check endpoint for monitoring the web gateway.
Response:
```json
{
- "success": true,
- "sessionId": "uuid"
+ "status": "healthy",
+ "version": "1.0.0",
+ "uptime": 12345
+}
+```
+
+#### GET /api/broker-status
+Check if RdpBroker service is available.
+
+Response:
+```json
+{
+ "available": true,
+ "broker": "rdpbroker:3389",
+ "timestamp": "2025-12-04T10:30:00.000Z"
}
```
#### GET /api/targets
-Get available RDP targets (requires X-Session-ID header).
+Fetch available RDP targets.
-Response:
+**Success Response (200):**
```json
{
"targets": [
{
- "name": "Server 01",
- "host": "192.168.1.10",
+ "name": "Windows Server 2022",
+ "host": "ws2022.example.com",
"port": 3389,
- "description": "Production Server"
+ "description": "Production Windows Server"
}
- ]
+ ],
+ "timestamp": "2025-12-04T10:30:00.000Z"
}
```
-#### POST /api/auth/logout
-Logout and destroy session (requires X-Session-ID header).
+**Service Unavailable (503):**
+```json
+{
+ "error": "RdpBroker service is unavailable. Please contact your administrator.",
+ "timestamp": "2025-12-04T10:30:00.000Z"
+}
+```
### WebSocket Protocol
@@ -126,7 +153,8 @@ Connect to `ws://localhost:8080/ws/rdp`
```json
{
"type": "connect",
- "sessionId": "uuid",
+ "username": "user@domain.com",
+ "password": "password123",
"target": {
"name": "Server 01",
"host": "192.168.1.10",
@@ -209,20 +237,21 @@ helm install rdp-web-gateway ./chart/rdp-web-gateway -n rdpbroker
- Firefox 88+
- Safari 14+
- Opera 76+
-
## Security Considerations
- Use HTTPS/WSS in production
-- Implement rate limiting
-- Set strong session secrets
+- Credentials are passed directly to RdpBroker (no storage in web-gateway)
+- Implement rate limiting at ingress level
- Enable CORS restrictions
- Regular security audits
-
+- All authentication handled by RdpBroker → Samba ADs
+- Regular security audits
## Performance Tuning
-- Adjust session timeout based on usage
- Configure WebSocket buffer sizes
-- Enable compression for HTTP responses
+- Use CDN for static assets in production
+- Enable HTTP compression (already included)
+- Adjust resource limits in Kubernetes
- Use CDN for static assets in production
## Troubleshooting
@@ -253,9 +282,21 @@ location /ws/ {
proxy_set_header Connection "upgrade";
}
```
-
### High memory usage
+Adjust resource limits in Kubernetes values.yaml
+
+## Logging
+
+All logs go to stdout/stderr for Kubernetes:
+
+```bash
+# View logs
+kubectl logs -f deployment/rdp-web-gateway -n rdpbroker
+
+# Follow logs for all pods
+kubectl logs -f -l app=rdp-web-gateway -n rdpbroker
+```
Reduce session timeout or implement session limits per user.
## Development
diff --git a/web-gateway/chart/rdp-web-gateway/templates/NOTES.txt b/web-gateway/chart/rdp-web-gateway/templates/NOTES.txt
index 5420906..c5dc58c 100644
--- a/web-gateway/chart/rdp-web-gateway/templates/NOTES.txt
+++ b/web-gateway/chart/rdp-web-gateway/templates/NOTES.txt
@@ -33,5 +33,6 @@
Configuration:
- RDP Broker: {{ .Values.config.rdpBroker.host }}:{{ .Values.config.rdpBroker.port }}
- Server Port: {{ .Values.config.server.port }}
-- Log Level: {{ .Values.config.server.logLevel }}
- Replicas: {{ if .Values.autoscaling.enabled }}{{ .Values.autoscaling.minReplicas }}-{{ .Values.autoscaling.maxReplicas }} (autoscaling){{ else }}{{ .Values.replicaCount }}{{ end }}
+
+Note: Authentication is handled by RdpBroker. Logs are sent to stdout for Kubernetes.
diff --git a/web-gateway/chart/rdp-web-gateway/templates/configmap.yaml b/web-gateway/chart/rdp-web-gateway/templates/configmap.yaml
index 5470418..7220ee4 100644
--- a/web-gateway/chart/rdp-web-gateway/templates/configmap.yaml
+++ b/web-gateway/chart/rdp-web-gateway/templates/configmap.yaml
@@ -12,10 +12,6 @@ data:
"port": {{ .Values.config.rdpBroker.port }}
},
"server": {
- "port": {{ .Values.config.server.port }},
- "logLevel": "{{ .Values.config.server.logLevel }}"
- },
- "session": {
- "timeout": {{ .Values.config.session.timeout }}
+ "port": {{ .Values.config.server.port }}
}
}
diff --git a/web-gateway/chart/rdp-web-gateway/templates/deployment.yaml b/web-gateway/chart/rdp-web-gateway/templates/deployment.yaml
index 7255b2f..3982a2a 100644
--- a/web-gateway/chart/rdp-web-gateway/templates/deployment.yaml
+++ b/web-gateway/chart/rdp-web-gateway/templates/deployment.yaml
@@ -41,23 +41,16 @@ spec:
value: {{ .Values.config.rdpBroker.host | quote }}
- name: RDP_BROKER_PORT
value: {{ .Values.config.rdpBroker.port | quote }}
- - name: LOG_LEVEL
- value: {{ .Values.config.server.logLevel | quote }}
- - name: SESSION_TIMEOUT
- value: {{ .Values.config.session.timeout | quote }}
+ {{- if .Values.config.rdpTargets }}
+ - name: RDP_TARGETS
+ value: {{ .Values.config.rdpTargets | toJson | quote }}
+ {{- end }}
- name: NODE_ENV
value: "production"
{{- range .Values.env }}
- name: {{ .name }}
value: {{ .value | quote }}
{{- end }}
- {{- if .Values.secrets.sessionSecret }}
- - name: SESSION_SECRET
- valueFrom:
- secretKeyRef:
- name: {{ include "rdp-web-gateway.fullname" . }}-secrets
- key: sessionSecret
- {{- end }}
ports:
- name: http
containerPort: {{ .Values.config.server.port }}
@@ -68,17 +61,6 @@ spec:
{{- toYaml .Values.readinessProbe | nindent 12 }}
resources:
{{- toYaml .Values.resources | nindent 12 }}
- {{- if .Values.persistence.enabled }}
- volumeMounts:
- - name: logs
- mountPath: {{ .Values.persistence.mountPath }}
- {{- end }}
- {{- if .Values.persistence.enabled }}
- volumes:
- - name: logs
- persistentVolumeClaim:
- claimName: {{ include "rdp-web-gateway.fullname" . }}-logs
- {{- end }}
{{- with .Values.nodeSelector }}
nodeSelector:
{{- toYaml . | nindent 8 }}
diff --git a/web-gateway/chart/rdp-web-gateway/templates/pvc.yaml b/web-gateway/chart/rdp-web-gateway/templates/pvc.yaml
deleted file mode 100644
index 0f3f1f8..0000000
--- a/web-gateway/chart/rdp-web-gateway/templates/pvc.yaml
+++ /dev/null
@@ -1,17 +0,0 @@
-{{- if .Values.persistence.enabled }}
-apiVersion: v1
-kind: PersistentVolumeClaim
-metadata:
- name: {{ include "rdp-web-gateway.fullname" . }}-logs
- labels:
- {{- include "rdp-web-gateway.labels" . | nindent 4 }}
-spec:
- accessModes:
- - {{ .Values.persistence.accessMode }}
- resources:
- requests:
- storage: {{ .Values.persistence.size }}
- {{- if .Values.persistence.storageClass }}
- storageClassName: {{ .Values.persistence.storageClass }}
- {{- end }}
-{{- end }}
diff --git a/web-gateway/chart/rdp-web-gateway/templates/secret.yaml b/web-gateway/chart/rdp-web-gateway/templates/secret.yaml
deleted file mode 100644
index 67b813e..0000000
--- a/web-gateway/chart/rdp-web-gateway/templates/secret.yaml
+++ /dev/null
@@ -1,13 +0,0 @@
-{{- if .Values.secrets }}
-apiVersion: v1
-kind: Secret
-metadata:
- name: {{ include "rdp-web-gateway.fullname" . }}-secrets
- labels:
- {{- include "rdp-web-gateway.labels" . | nindent 4 }}
-type: Opaque
-data:
- {{- if .Values.secrets.sessionSecret }}
- sessionSecret: {{ .Values.secrets.sessionSecret | b64enc | quote }}
- {{- end }}
-{{- end }}
diff --git a/web-gateway/chart/rdp-web-gateway/values.yaml b/web-gateway/chart/rdp-web-gateway/values.yaml
index 6b8fd47..c4317a6 100644
--- a/web-gateway/chart/rdp-web-gateway/values.yaml
+++ b/web-gateway/chart/rdp-web-gateway/values.yaml
@@ -82,29 +82,26 @@ config:
# Server configuration
server:
port: 8080
- logLevel: "info"
- # Session configuration
- session:
- timeout: 3600000 # 1 hour in milliseconds
+ # Optional: Pre-configure RDP targets
+ # If not set, targets will be managed by RdpBroker
+ # Format: JSON array of target objects
+ rdpTargets: null
+ # Example:
+ # - name: "Windows Server 2022"
+ # host: "ws2022.example.com"
+ # port: 3389
+ # description: "Production Windows Server"
+ # - name: "Development Server"
+ # host: "dev.example.com"
+ # port: 3389
+ # description: "Development environment"
# Environment variables
env: []
# - name: CUSTOM_VAR
# value: "value"
-# Secrets (for sensitive configuration)
-secrets: {}
- # sessionSecret: "your-secret-key"
-
-# Persistence for logs
-persistence:
- enabled: false
- storageClass: ""
- accessMode: ReadWriteOnce
- size: 5Gi
- mountPath: /var/log/rdp-web-gateway
-
# Liveness and readiness probes
livenessProbe:
httpGet:
diff --git a/web-gateway/package.json b/web-gateway/package.json
index 70f65e5..5bff03d 100644
--- a/web-gateway/package.json
+++ b/web-gateway/package.json
@@ -20,13 +20,10 @@
"dependencies": {
"express": "^4.18.2",
"ws": "^8.14.2",
- "node-rdpjs": "^0.3.2",
"dotenv": "^16.3.1",
"compression": "^1.7.4",
"helmet": "^7.1.0",
- "cors": "^2.8.5",
- "uuid": "^9.0.1",
- "winston": "^3.11.0"
+ "cors": "^2.8.5"
},
"devDependencies": {
"nodemon": "^3.0.2"
diff --git a/web-gateway/public/js/app.js b/web-gateway/public/js/app.js
index 019079a..8944ef7 100644
--- a/web-gateway/public/js/app.js
+++ b/web-gateway/public/js/app.js
@@ -5,7 +5,7 @@ class RDPWebGateway {
this.ctx = null;
this.currentUser = null;
this.currentTarget = null;
- this.sessionId = null;
+ this.credentials = null;
this.init();
}
@@ -63,27 +63,24 @@ class RDPWebGateway {
errorMessage.style.display = 'none';
try {
- const response = await fetch('/api/auth/login', {
- method: 'POST',
- headers: {
- 'Content-Type': 'application/json',
- },
- body: JSON.stringify({ username, password }),
- });
-
- const data = await response.json();
-
- if (response.ok) {
- this.currentUser = username;
- this.sessionId = data.sessionId;
- await this.loadTargets();
- this.showTargetsView();
- } else {
- this.showError(errorMessage, data.error || 'Authentication failed');
+ // Check if RdpBroker service is available
+ const statusResponse = await fetch('/api/broker-status');
+ const statusData = await statusResponse.json();
+
+ if (!statusData.available) {
+ this.showError(errorMessage, 'RDP service is currently unavailable. Please contact your administrator.');
+ return;
}
+
+ // Store credentials temporarily - will be sent to RdpBroker
+ this.currentUser = username;
+ this.credentials = { username, password };
+
+ // Load targets and show targets view
+ await this.loadTargets();
} catch (error) {
console.error('Login error:', error);
- this.showError(errorMessage, 'Connection error. Please try again.');
+ this.showError(errorMessage, 'Connection error. Please check your network and try again.');
} finally {
loginBtn.disabled = false;
btnText.style.display = 'block';
@@ -93,25 +90,45 @@ class RDPWebGateway {
async loadTargets() {
try {
- const response = await fetch('/api/targets', {
- headers: {
- 'X-Session-ID': this.sessionId,
- },
- });
-
+ const response = await fetch('/api/targets');
+
if (!response.ok) {
+ if (response.status === 503) {
+ const error = await response.json();
+ throw new Error(error.error || 'Service unavailable');
+ }
throw new Error('Failed to load targets');
}
const data = await response.json();
- this.displayTargets(data.targets);
+ this.showTargetsView(data.targets);
} catch (error) {
- console.error('Load targets error:', error);
- const targetsError = document.getElementById('targetsError');
- this.showError(targetsError, 'Failed to load available desktops');
+ console.error('Error loading targets:', error);
+ // Show error in targets view
+ this.showTargetsView(null, error.message);
}
}
+ showTargetsView(targets = null, errorMsg = null) {
+ document.getElementById('loginCard').style.display = 'none';
+ document.getElementById('targetsCard').style.display = 'block';
+ document.getElementById('rdpViewer').style.display = 'none';
+ document.getElementById('currentUser').textContent = this.currentUser;
+
+ if (errorMsg) {
+ const targetsList = document.getElementById('targetsList');
+ targetsList.innerHTML = `
+
+
⚠️ ${this.escapeHtml(errorMsg)}
+
+
+ `;
+ return;
+ }
+
+ this.displayTargets(targets);
+ }
+
displayTargets(targets) {
const targetsList = document.getElementById('targetsList');
targetsList.innerHTML = '';
@@ -153,10 +170,11 @@ class RDPWebGateway {
this.ws.onopen = () => {
console.log('WebSocket connected');
- // Send connection request
+ // Send credentials and connection request to RdpBroker
this.ws.send(JSON.stringify({
type: 'connect',
- sessionId: this.sessionId,
+ username: this.credentials.username,
+ password: this.credentials.password,
target: target,
}));
};
@@ -324,7 +342,7 @@ class RDPWebGateway {
}
this.currentUser = null;
this.currentTarget = null;
- this.sessionId = null;
+ this.credentials = null;
this.showLoginView();
}
@@ -336,13 +354,6 @@ class RDPWebGateway {
document.getElementById('password').value = '';
}
- showTargetsView() {
- document.getElementById('loginCard').style.display = 'none';
- document.getElementById('targetsCard').style.display = 'block';
- document.getElementById('rdpViewer').style.display = 'none';
- document.getElementById('currentUser').textContent = this.currentUser;
- }
-
showRDPViewer() {
document.getElementById('loginCard').style.display = 'none';
document.getElementById('targetsCard').style.display = 'none';
diff --git a/web-gateway/src/logger.js b/web-gateway/src/logger.js
deleted file mode 100644
index e332f2f..0000000
--- a/web-gateway/src/logger.js
+++ /dev/null
@@ -1,41 +0,0 @@
-const winston = require('winston');
-
-const logger = winston.createLogger({
- level: process.env.LOG_LEVEL || 'info',
- format: winston.format.combine(
- winston.format.timestamp({
- format: 'YYYY-MM-DD HH:mm:ss'
- }),
- winston.format.errors({ stack: true }),
- winston.format.splat(),
- winston.format.json()
- ),
- defaultMeta: { service: 'rdp-web-gateway' },
- transports: [
- // Write to console
- new winston.transports.Console({
- format: winston.format.combine(
- winston.format.colorize(),
- winston.format.printf(({ timestamp, level, message, ...metadata }) => {
- let msg = `${timestamp} [${level}]: ${message}`;
- if (Object.keys(metadata).length > 0) {
- msg += ` ${JSON.stringify(metadata)}`;
- }
- return msg;
- })
- )
- }),
- // Write to file
- new winston.transports.File({
- filename: '/var/log/rdp-web-gateway/error.log',
- level: 'error',
- handleExceptions: true
- }),
- new winston.transports.File({
- filename: '/var/log/rdp-web-gateway/combined.log',
- handleExceptions: true
- })
- ]
-});
-
-module.exports = logger;
diff --git a/web-gateway/src/rdpProxyHandler.js b/web-gateway/src/rdpProxyHandler.js
index 9fcb52f..7c71986 100644
--- a/web-gateway/src/rdpProxyHandler.js
+++ b/web-gateway/src/rdpProxyHandler.js
@@ -1,13 +1,10 @@
const net = require('net');
-const logger = require('./logger');
class RDPProxyHandler {
- constructor(websocket, sessionManager, rdpBrokerHost, rdpBrokerPort) {
+ constructor(websocket, rdpBrokerHost, rdpBrokerPort) {
this.ws = websocket;
- this.sessionManager = sessionManager;
this.rdpBrokerHost = rdpBrokerHost;
this.rdpBrokerPort = rdpBrokerPort;
- this.session = null;
this.rdpSocket = null;
this.isConnected = false;
}
@@ -28,40 +25,33 @@ class RDPProxyHandler {
this.handleSpecialCommand(message);
break;
default:
- logger.warn(`Unknown message type: ${message.type}`);
+ console.warn(`${new Date().toISOString()} [WARN] Unknown message type: ${message.type}`);
}
} catch (error) {
- logger.error('Error handling message:', error);
+ console.error(`${new Date().toISOString()} [ERROR] Error handling message:`, error);
this.sendError('Failed to process message');
}
}
async handleConnect(message) {
- const { sessionId, target } = message;
+ const { username, password, target } = message;
- // Validate session
- this.session = this.sessionManager.getSession(sessionId);
- if (!this.session) {
- return this.sendError('Invalid session');
- }
-
- logger.info(`Connecting to RDP Broker for session ${sessionId}, target: ${target.name}`);
+ console.log(`${new Date().toISOString()} [INFO] Connecting to RDP Broker, target: ${target?.name || 'unknown'}`);
try {
// Connect to RDP Broker
this.rdpSocket = new net.Socket();
this.rdpSocket.connect(this.rdpBrokerPort, this.rdpBrokerHost, () => {
- logger.info(`Connected to RDP Broker at ${this.rdpBrokerHost}:${this.rdpBrokerPort}`);
+ console.log(`${new Date().toISOString()} [INFO] Connected to RDP Broker at ${this.rdpBrokerHost}:${this.rdpBrokerPort}`);
this.isConnected = true;
- // Send authentication to RdpBroker
- // In real implementation, this would follow the RDP protocol
- this.sendAuthToBroker();
+ // Send credentials to RdpBroker (it will handle authentication)
+ this.sendAuthToBroker(username, password);
this.ws.send(JSON.stringify({
type: 'connected',
- target: target.name
+ target: target?.name || 'RDP Server'
}));
// Set canvas size
@@ -75,20 +65,19 @@ class RDPProxyHandler {
// Handle data from RDP Broker
this.rdpSocket.on('data', (data) => {
// Forward RDP data to WebSocket client
- // In production, this would be properly decoded RDP frames
if (this.ws.readyState === 1) { // WebSocket.OPEN
this.ws.send(data);
}
});
this.rdpSocket.on('error', (error) => {
- logger.error('RDP socket error:', error);
+ console.error(`${new Date().toISOString()} [ERROR] RDP socket error:`, error);
this.sendError('Connection to RDP broker failed');
this.cleanup();
});
this.rdpSocket.on('close', () => {
- logger.info('RDP connection closed');
+ console.log(`${new Date().toISOString()} [INFO] RDP connection closed`);
this.isConnected = false;
if (this.ws.readyState === 1) {
this.ws.close();
@@ -96,25 +85,19 @@ class RDPProxyHandler {
});
} catch (error) {
- logger.error('Connection error:', error);
+ console.error(`${new Date().toISOString()} [ERROR] Connection error:`, error);
this.sendError('Failed to connect to RDP broker');
}
}
- sendAuthToBroker() {
- // This is a simplified version
- // In production, implement proper RDP protocol handshake
+ sendAuthToBroker(username, password) {
+ // Send credentials directly to RdpBroker
+ // RdpBroker will handle Samba AD authentication
- if (!this.rdpSocket || !this.session) return;
+ if (!this.rdpSocket) return;
- const authData = {
- username: this.session.username,
- password: this.session.data.password
- };
-
- // Send authentication data
// Format: "Username: \nPassword: \n"
- const authMessage = `${authData.username}\n${authData.password}\n`;
+ const authMessage = `${username}\n${password}\n`;
this.rdpSocket.write(authMessage);
}
@@ -164,10 +147,10 @@ class RDPProxyHandler {
command: 'ctrl-alt-del'
});
this.rdpSocket.write(cadData + '\n');
- logger.info('Sent Ctrl+Alt+Del');
+ console.log(`${new Date().toISOString()} [INFO] Sent Ctrl+Alt+Del`);
break;
default:
- logger.warn(`Unknown special command: ${message.action}`);
+ console.warn(`${new Date().toISOString()} [WARN] Unknown special command: ${message.action}`);
}
}
@@ -186,12 +169,6 @@ class RDPProxyHandler {
this.rdpSocket = null;
}
this.isConnected = false;
-
- if (this.session) {
- this.sessionManager.updateSession(this.session.id, {
- rdpConnection: null
- });
- }
}
}
diff --git a/web-gateway/src/server.js b/web-gateway/src/server.js
index cafff07..4919b59 100644
--- a/web-gateway/src/server.js
+++ b/web-gateway/src/server.js
@@ -2,11 +2,10 @@ const express = require('express');
const http = require('http');
const WebSocket = require('ws');
const path = require('path');
+const net = require('net');
const compression = require('compression');
const helmet = require('helmet');
const cors = require('cors');
-const logger = require('./logger');
-const SessionManager = require('./sessionManager');
const RDPProxyHandler = require('./rdpProxyHandler');
class RDPWebGatewayServer {
@@ -18,7 +17,6 @@ class RDPWebGatewayServer {
path: '/ws/rdp'
});
- this.sessionManager = new SessionManager();
this.port = process.env.PORT || 8080;
this.rdpBrokerHost = process.env.RDP_BROKER_HOST || 'rdpbroker';
this.rdpBrokerPort = process.env.RDP_BROKER_PORT || 3389;
@@ -45,7 +43,7 @@ class RDPWebGatewayServer {
// Logging
this.app.use((req, res, next) => {
- logger.info(`${req.method} ${req.url}`);
+ console.log(`${new Date().toISOString()} [INFO] ${req.method} ${req.url}`);
next();
});
}
@@ -56,70 +54,44 @@ class RDPWebGatewayServer {
res.json({
status: 'healthy',
version: '1.0.0',
- sessions: this.sessionManager.getActiveSessionCount()
+ uptime: process.uptime()
});
});
- // Authentication endpoint
- this.app.post('/api/auth/login', async (req, res) => {
- try {
- const { username, password } = req.body;
-
- if (!username || !password) {
- return res.status(400).json({ error: 'Username and password required' });
- }
-
- // Create session
- const session = this.sessionManager.createSession(username, {
- password,
- ipAddress: req.ip
- });
-
- logger.info(`User ${username} authenticated, session: ${session.id}`);
-
- res.json({
- success: true,
- sessionId: session.id
- });
- } catch (error) {
- logger.error('Login error:', error);
- res.status(500).json({ error: 'Authentication failed' });
- }
+ // RdpBroker health check endpoint
+ this.app.get('/api/broker-status', async (req, res) => {
+ const isAvailable = await this.checkRdpBrokerHealth();
+ res.json({
+ available: isAvailable,
+ broker: `${this.rdpBrokerHost}:${this.rdpBrokerPort}`,
+ timestamp: new Date().toISOString()
+ });
});
- // Get available targets
+ // Get targets list
this.app.get('/api/targets', async (req, res) => {
try {
- const sessionId = req.headers['x-session-id'];
-
- if (!sessionId) {
- return res.status(401).json({ error: 'Session ID required' });
+ const isAvailable = await this.checkRdpBrokerHealth();
+ if (!isAvailable) {
+ return res.status(503).json({
+ error: 'RdpBroker service is unavailable. Please contact your administrator.',
+ timestamp: new Date().toISOString()
+ });
}
- const session = this.sessionManager.getSession(sessionId);
- if (!session) {
- return res.status(401).json({ error: 'Invalid session' });
- }
-
- // In a real implementation, this would fetch from RdpBroker
- // For now, return static list
- const targets = await this.fetchTargetsFromBroker(session);
-
- res.json({ targets });
+ // Parse targets from environment variable
+ const targets = this.parseTargetsFromEnv();
+ res.json({ targets, timestamp: new Date().toISOString() });
} catch (error) {
- logger.error('Targets fetch error:', error);
- res.status(500).json({ error: 'Failed to fetch targets' });
+ console.error(`${new Date().toISOString()} [ERROR] Error fetching targets:`, error);
+ res.status(500).json({
+ error: 'Failed to fetch targets',
+ timestamp: new Date().toISOString()
+ });
}
});
- // Logout
- this.app.post('/api/auth/logout', (req, res) => {
- const sessionId = req.headers['x-session-id'];
- if (sessionId) {
- this.sessionManager.destroySession(sessionId);
- }
- res.json({ success: true });
- });
+ // API endpoints are removed - authentication handled by RdpBroker
// Catch all - serve index.html
this.app.get('*', (req, res) => {
@@ -129,11 +101,10 @@ class RDPWebGatewayServer {
setupWebSocket() {
this.wss.on('connection', (ws, req) => {
- logger.info('New WebSocket connection');
+ console.log(`${new Date().toISOString()} [INFO] New WebSocket connection`);
const proxyHandler = new RDPProxyHandler(
- ws,
- this.sessionManager,
+ ws,
this.rdpBrokerHost,
this.rdpBrokerPort
);
@@ -143,7 +114,7 @@ class RDPWebGatewayServer {
const message = JSON.parse(data.toString());
proxyHandler.handleMessage(message);
} catch (error) {
- logger.error('WebSocket message error:', error);
+ console.error(`${new Date().toISOString()} [ERROR] WebSocket message error:`, error);
ws.send(JSON.stringify({
type: 'error',
error: 'Invalid message format'
@@ -152,50 +123,71 @@ class RDPWebGatewayServer {
});
ws.on('close', () => {
- logger.info('WebSocket connection closed');
+ console.log(`${new Date().toISOString()} [INFO] WebSocket connection closed`);
proxyHandler.cleanup();
});
ws.on('error', (error) => {
- logger.error('WebSocket error:', error);
+ console.error(`${new Date().toISOString()} [ERROR] WebSocket error:`, error);
proxyHandler.cleanup();
});
});
}
- async fetchTargetsFromBroker(session) {
- // This is a simplified version
- // In production, this would communicate with RdpBroker
- // to get the actual list of targets
-
- // For now, return example targets
- return [
- {
- name: "Windows Server 01",
- host: "192.168.1.10",
+
+
+ // Check if RdpBroker is available
+ checkRdpBrokerHealth() {
+ return new Promise((resolve) => {
+ const socket = new net.Socket();
+ const timeout = 3000; // 3 second timeout
+
+ socket.setTimeout(timeout);
+
+ socket.on('connect', () => {
+ socket.destroy();
+ resolve(true);
+ });
+
+ socket.on('timeout', () => {
+ socket.destroy();
+ resolve(false);
+ });
+
+ socket.on('error', () => {
+ resolve(false);
+ });
+
+ socket.connect(this.rdpBrokerPort, this.rdpBrokerHost);
+ });
+ }
+
+ // Parse targets from environment variable
+ parseTargetsFromEnv() {
+ const targetsEnv = process.env.RDP_TARGETS;
+ if (!targetsEnv) {
+ // Return default message if no targets configured
+ return [{
+ name: 'Default',
+ host: 'via-rdpbroker',
port: 3389,
- description: "Production Web Server"
- },
- {
- name: "Windows Server 02",
- host: "192.168.1.11",
- port: 3389,
- description: "Database Server"
- },
- {
- name: "Development Desktop",
- host: "dev-machine.local",
- port: 3389,
- description: "Developer Workstation"
- }
- ];
+ description: 'Targets will be provided by RdpBroker'
+ }];
+ }
+
+ try {
+ return JSON.parse(targetsEnv);
+ } catch (error) {
+ console.error(`${new Date().toISOString()} [ERROR] Failed to parse RDP_TARGETS:`, error);
+ return [];
+ }
}
start() {
this.server.listen(this.port, () => {
- logger.info(`RDP Web Gateway server running on port ${this.port}`);
- logger.info(`RDP Broker: ${this.rdpBrokerHost}:${this.rdpBrokerPort}`);
- logger.info(`WebSocket endpoint: ws://localhost:${this.port}/ws/rdp`);
+ console.log(`${new Date().toISOString()} [INFO] RDP Web Gateway server running on port ${this.port}`);
+ console.log(`${new Date().toISOString()} [INFO] RDP Broker: ${this.rdpBrokerHost}:${this.rdpBrokerPort}`);
+ console.log(`${new Date().toISOString()} [INFO] WebSocket endpoint: ws://localhost:${this.port}/ws/rdp`);
});
}
}
@@ -206,9 +198,9 @@ server.start();
// Graceful shutdown
process.on('SIGTERM', () => {
- logger.info('SIGTERM received, shutting down gracefully...');
+ console.log(`${new Date().toISOString()} [INFO] SIGTERM received, shutting down gracefully...`);
server.server.close(() => {
- logger.info('Server closed');
+ console.log(`${new Date().toISOString()} [INFO] Server closed`);
process.exit(0);
});
});
diff --git a/web-gateway/src/sessionManager.js b/web-gateway/src/sessionManager.js
deleted file mode 100644
index 43994ea..0000000
--- a/web-gateway/src/sessionManager.js
+++ /dev/null
@@ -1,94 +0,0 @@
-const { v4: uuidv4 } = require('uuid');
-const logger = require('./logger');
-
-class SessionManager {
- constructor() {
- this.sessions = new Map();
- this.sessionTimeout = 3600000; // 1 hour
-
- // Cleanup inactive sessions every 5 minutes
- setInterval(() => this.cleanupSessions(), 300000);
- }
-
- createSession(username, userData = {}) {
- const sessionId = uuidv4();
- const session = {
- id: sessionId,
- username,
- createdAt: Date.now(),
- lastActivity: Date.now(),
- data: userData,
- rdpConnection: null
- };
-
- this.sessions.set(sessionId, session);
- logger.info(`Session created: ${sessionId} for user ${username}`);
-
- return session;
- }
-
- getSession(sessionId) {
- const session = this.sessions.get(sessionId);
- if (session) {
- session.lastActivity = Date.now();
- }
- return session;
- }
-
- updateSession(sessionId, data) {
- const session = this.sessions.get(sessionId);
- if (session) {
- session.data = { ...session.data, ...data };
- session.lastActivity = Date.now();
- }
- return session;
- }
-
- destroySession(sessionId) {
- const session = this.sessions.get(sessionId);
- if (session) {
- logger.info(`Session destroyed: ${sessionId}`);
-
- // Cleanup any active RDP connection
- if (session.rdpConnection) {
- session.rdpConnection.close();
- }
-
- this.sessions.delete(sessionId);
- return true;
- }
- return false;
- }
-
- cleanupSessions() {
- const now = Date.now();
- let cleaned = 0;
-
- for (const [sessionId, session] of this.sessions.entries()) {
- if (now - session.lastActivity > this.sessionTimeout) {
- logger.info(`Cleaning up inactive session: ${sessionId}`);
- this.destroySession(sessionId);
- cleaned++;
- }
- }
-
- if (cleaned > 0) {
- logger.info(`Cleaned up ${cleaned} inactive session(s)`);
- }
- }
-
- getActiveSessionCount() {
- return this.sessions.size;
- }
-
- getAllSessions() {
- return Array.from(this.sessions.values()).map(session => ({
- id: session.id,
- username: session.username,
- createdAt: session.createdAt,
- lastActivity: session.lastActivity
- }));
- }
-}
-
-module.exports = SessionManager;