Back to Playground
Self-Hosted Deployment
Deploy a secure, containerized SDK terminal with full Docker isolation for your users.
Backend Server Setup
1. Install Dependencies
package.json
npm install express ws dockerode uuid2. Docker Terminal Server
Create a Node.js server that manages isolated Docker containers for each user session.
docker-terminal-server.js
const express = require('express');
const WebSocket = require('ws');
const Docker = require('dockerode');
const { v4: uuidv4 } = require('uuid');
const app = express();
const docker = new Docker();
const wss = new WebSocket.Server({ noServer: true });
const sessions = new Map();
const CONTAINER_CONFIG = {
Image: 'ubuntu:22.04',
Cmd: ['/bin/bash'],
Tty: true,
OpenStdin: true,
Memory: 256 * 1024 * 1024, // 256MB limit
HostConfig: {
Memory: 256 * 1024 * 1024,
CPUQuota: 50000, // 50% CPU
PidsLimit: 1024,
NetworkMode: 'none', // No network
ReadonlyRootfs: true,
SecurityOpt: ['no-new-privileges:true'],
CapDrop: ['ALL'],
},
Env: [
'TERM=xterm-256color',
'PS1=\\[\\e[32m\\]sandbox$ \\[\\e[0m\\]',
]
};
async function createSandboxContainer() {
const containerId = uuidv4().slice(0, 12);
const container = await docker.createContainer({
...CONTAINER_CONFIG,
name: `terminal-${containerId}`,
});
await container.start();
return { containerId, container };
}
wss.on('connection', async (ws) => {
const sandbox = await createSandboxContainer();
const stream = await sandbox.container.attach({
stream: true,
stdin: true,
stdout: true,
stderr: true,
tty: true
});
ws.on('message', (data) => {
const msg = JSON.parse(data);
if (msg.type === 'input') {
stream.write(msg.data);
}
});
stream.on('data', (chunk) => {
ws.send(JSON.stringify({
type: 'output',
data: chunk.toString('utf8')
}));
});
ws.on('close', async () => {
await sandbox.container.kill();
await sandbox.container.remove();
});
});
const server = app.listen(3000);
server.on('upgrade', (req, socket, head) => {
wss.handleUpgrade(req, socket, head, (ws) => {
wss.emit('connection', ws);
});
});
console.log('🐳 Server running on http://localhost:3000');3. Security Hardening
Apply Docker daemon lockdown and AppArmor profiles.
security-hardening.sh
#!/bin/bash
# Docker daemon lockdown
cat > /etc/docker/daemon.json << EOF
{
"icc": false,
"userns-remap": "default",
"no-new-privileges": true
}
EOF
# AppArmor profile
cat > /etc/apparmor.d/docker-terminal << EOF
profile docker-terminal flags=(attach_disconnected) {
deny /etc/** r,
deny /proc/[1-9]**/[ns]last_pid -> r,
deny /sys/** r,
}
EOF
systemctl restart docker apparmorFrontend Integration
WebSocket Terminal Client
WebTerminal.ts
class WebTerminal {
private ws: WebSocket;
private term: any; // xterm.js instance
connect(url: string) {
this.ws = new WebSocket(url);
this.ws.onmessage = (event) => {
const msg = JSON.parse(event.data);
if (msg.type === 'output') {
this.term.write(msg.data);
}
};
this.term.onData((data: string) => {
this.ws.send(JSON.stringify({
type: 'input',
data
}));
});
}
close() {
this.ws.close();
}
}Security Guarantees
Network Isolation
CriticalNetworkMode: none prevents all outbound connections
Memory Limit
Critical256MB hard cap prevents memory exhaustion
CPU Quota
High50% CPU limit prevents resource starvation
Filesystem
HighRead-only rootfs prevents file modifications
Capabilities
CriticalCAP_DROP: ALL prevents privilege escalation
PID Limit
Medium1024 process limit prevents fork bombs
Deployment Steps
Deploy.sh
# 1. Clone your project
git clone https://github.com/x2yDevs/x2y-playground.git
cd x2y-playground
# 2. Install dependencies
npm install
# 3. Apply security hardening
chmod +x security-hardening.sh
sudo ./security-hardening.sh
# 4. Start the server
node docker-terminal-server.js
# 5. Access at http://localhost:3000
# Each WebSocket connection creates isolated container