Files
cloudron-box/src/dockerproxy.js

110 lines
3.2 KiB
JavaScript
Raw Normal View History

2018-08-13 21:10:53 +02:00
'use strict';
exports = module.exports = {
start: start,
stop: stop
};
var assert = require('assert'),
config = require('./config.js'),
2018-08-14 18:27:08 -07:00
express = require('express'),
2018-08-13 22:06:28 +02:00
debug = require('debug')('box:dockerproxy'),
2018-08-13 22:14:56 +02:00
http = require('http'),
2018-08-14 18:27:08 -07:00
middleware = require('./middleware'),
2018-08-13 22:14:56 +02:00
net = require('net');
2018-08-13 21:10:53 +02:00
2018-08-14 19:03:10 -07:00
var gHttpServer = null;
2018-08-13 21:10:53 +02:00
2018-08-14 18:27:08 -07:00
function authorizeApp(req, res, next) {
// TODO add here some authorization
// - block apps not using the docker addon
// - block calls regarding platform containers
// - only allow managing and inspection of containers belonging to the app
2018-08-13 21:10:53 +02:00
2018-08-14 18:27:08 -07:00
return next();
}
2018-08-13 22:14:56 +02:00
2018-08-14 18:27:08 -07:00
function attachDockerRequest(req, res, next) {
var options = {
socketPath: '/var/run/docker.sock',
method: req.method,
path: req.url,
headers: req.headers
};
2018-08-13 22:14:56 +02:00
2018-08-14 18:27:08 -07:00
req.dockerRequest = http.request(options, function (dockerResponse) {
res.writeHead(dockerResponse.statusCode, dockerResponse.headers);
2018-08-13 21:10:53 +02:00
2018-08-14 18:27:08 -07:00
// Force node to send out the headers, this is required for the /container/wait api to make the docker cli proceed
res.write(' ');
2018-08-13 21:10:53 +02:00
2018-08-14 18:27:08 -07:00
dockerResponse.on('error', function (error) { console.error('dockerResponse error:', error); });
dockerResponse.pipe(res, { end: true });
});
2018-08-13 21:10:53 +02:00
2018-08-14 18:27:08 -07:00
next();
}
2018-08-14 18:27:08 -07:00
function containersCreate(req, res, next) {
// overwrite the network the container lives in
req.body.HostConfig.NetworkMode = 'cloudron';
2018-08-13 22:14:56 +02:00
2018-08-14 18:27:08 -07:00
var plainBody = JSON.stringify(req.body);
2018-08-13 22:14:56 +02:00
2018-08-14 18:27:08 -07:00
req.dockerRequest.setHeader('Content-Length', Buffer.byteLength(plainBody));
req.dockerRequest.end(plainBody);
}
function process(req, res, next) {
if (!req.readable) {
req.dockerRequest.end();
} else {
req.pipe(req.dockerRequest, { end: true });
}
}
function start(callback) {
assert.strictEqual(typeof callback, 'function');
2018-08-14 19:03:10 -07:00
assert(gHttpServer === null, 'Already started');
2018-08-13 21:10:53 +02:00
2018-08-14 18:27:08 -07:00
let json = middleware.json({ strict: true });
let router = new express.Router();
router.post('/:version/containers/create', json, containersCreate); // only available until no-domain
2018-08-13 22:14:56 +02:00
2018-08-14 18:27:08 -07:00
var proxyServer = express();
proxyServer.use(authorizeApp)
.use(attachDockerRequest)
.use(router)
.use(process);
2018-08-14 19:03:10 -07:00
gHttpServer = http.createServer(proxyServer);
gHttpServer.listen(config.get('dockerProxyPort'), callback);
2018-08-14 18:27:08 -07:00
debug(`startDockerProxy: started proxy on port ${config.get('dockerProxyPort')}`);
2018-08-13 22:14:56 +02:00
2018-08-14 19:03:10 -07:00
gHttpServer.on('upgrade', function (req, client, head) {
2018-08-13 22:14:56 +02:00
// Create a new tcp connection to the TCP server
var remote = net.connect('/var/run/docker.sock', function () {
// two-way pipes between client and docker daemon
client.pipe(remote).pipe(client);
// resend the upgrade event to the docker daemon, so it responds with the proper message through the pipes
remote.write(req.method + ' ' + req.url + ' HTTP/1.1\r\n' +
`Host: ${req.headers.host}\r\n` +
'Connection: Upgrade\r\n' +
'Upgrade: tcp\r\n' +
'\r\n'
);
});
});
2018-08-13 21:10:53 +02:00
}
function stop(callback) {
assert.strictEqual(typeof callback, 'function');
2018-08-14 19:03:10 -07:00
if (gHttpServer) gHttpServer.close();
gHttpServer = null;
2018-08-13 21:10:53 +02:00
callback();
}