I am working on a new project where I need to connect to a docker container from web? By connect I mean I will create a shell in the website and tat shell will be able to connect to the running container.
I am not able to figure out how to proceed with the project. Can anyone help me out?
CodePudding user response:
From what I know there is no out of the box solution, you should make your own api executing command on your container running docker exec <id> <command>
and returning the output, mind escape the command.
However you should know that letting a user run commands inside a docker is dangerous as it could impact your host.
CodePudding user response:
You should be able to use Docker APIs for this (https://docs.docker.com/engine/api/) along with a framework that wraps the APIs. For example refer dockerode.
CodePudding user response:
You could combine some JavaScript modules. node-pty and xterm being the most important. Additionally, ws is useful, but could be replaced but something else.
Note: this is not a production ready example. In particular, you should take care of safety measurements, or better yet, let no one but yourself use it.
server.js
import { WebSocketServer, createWebSocketStream } from 'ws';
import pty from 'node-pty';
const wss = new WebSocketServer({ port: 3000 });
wss.on('connection', (ws) => {
console.log('new connection');
const duplex = createWebSocketStream(ws, { encoding: 'utf8' });
const proc = pty.spawn('docker', ['run', "--rm", "-ti", "ubuntu", "bash"], {
name: 'xterm-color',
cwd: process.env.HOME,
env: process.env,
});
const onData = proc.onData((data) => duplex.write(data));
const exit = proc.onExit(() => {
console.log("process exited");
onData.dispose();
exit.dispose();
});
duplex.on('data', (data) => proc.write(data.toString()));
ws.on('close', function () {
console.log('stream closed');
proc.kill();
duplex.destroy();
});
});
index.html
<!doctype html>
<html>
<head>
<link rel="stylesheet" href="node_modules/xterm/css/xterm.css" />
<script src="node_modules/xterm/lib/xterm.js"></script>
</head>
<body>
<div id="terminal"></div>
<script type="module">
const term = new Terminal();
term.open(document.getElementById('terminal'));
const ws = new WebSocket('ws://localhost:3000');
ws.onmessage = async ({ data }) => term.write(await data.text());
term.onData((data) => ws.send(data));
</script>
</body>
</html>
Note: I am using the statics from the node modules folder, in your real code you probably want to use a bundler for this.
I am serving the entire project with nginx on port 8080 for simplicity. And then I start the server with node.
docker run -d --rm -p 8080:8080 -v "$PWD:/usr/share/nginx/html" nginxinc/nginx-unprivileged
node server.mjs
Afterwards, I can open http://localhost:8080 in my browser, and get a shell.
The dependencies in my package.json are these:
"dependencies": {
"node-pty": "^0.10.1",
"ws": "^8.7.0",
"xterm": "^4.18.0"
}
You can view the code in this repo: https://github.com/bluebrown/web-shell.