2018-08-13 21:10:53 +02:00
|
|
|
'use strict';
|
|
|
|
|
|
|
|
|
|
exports = module.exports = {
|
|
|
|
|
start: start,
|
|
|
|
|
stop: stop
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
var assert = require('assert'),
|
2018-08-13 22:14:56 +02:00
|
|
|
bodyParser = require('body-parser'),
|
2018-08-13 21:10:53 +02:00
|
|
|
config = require('./config.js'),
|
2018-08-13 22:06:28 +02:00
|
|
|
debug = require('debug')('box:dockerproxy'),
|
2018-08-13 22:14:56 +02:00
|
|
|
http = require('http'),
|
|
|
|
|
net = require('net');
|
2018-08-13 21:10:53 +02:00
|
|
|
|
|
|
|
|
var gServer = null;
|
2018-08-14 22:52:00 +02:00
|
|
|
var gJSONParser = bodyParser.json();
|
2018-08-13 21:10:53 +02:00
|
|
|
|
|
|
|
|
function start(callback) {
|
|
|
|
|
assert.strictEqual(typeof callback, 'function');
|
|
|
|
|
|
2018-08-13 22:14:56 +02:00
|
|
|
|
2018-08-14 22:52:00 +02:00
|
|
|
function authorized(req, res) {
|
|
|
|
|
// 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 22:14:56 +02:00
|
|
|
|
2018-08-14 22:52:00 +02:00
|
|
|
return true;
|
2018-08-13 21:10:53 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
debug(`startDockerProxy: starting proxy on port ${config.get('dockerProxyPort')}`);
|
|
|
|
|
|
|
|
|
|
gServer = http.createServer(function (req, res) {
|
2018-08-14 22:52:00 +02:00
|
|
|
if (!authorized(req, res)) return;
|
2018-08-13 21:10:53 +02:00
|
|
|
|
2018-08-13 22:01:51 +02:00
|
|
|
var options = {
|
|
|
|
|
socketPath: '/var/run/docker.sock',
|
|
|
|
|
method: req.method,
|
|
|
|
|
path: req.url,
|
2018-08-13 22:14:56 +02:00
|
|
|
headers: req.headers
|
2018-08-13 22:01:51 +02:00
|
|
|
};
|
|
|
|
|
|
2018-08-13 21:10:53 +02:00
|
|
|
var dockerRequest = http.request(options, function (dockerResponse) {
|
|
|
|
|
res.writeHead(dockerResponse.statusCode, dockerResponse.headers);
|
2018-08-13 22:14:56 +02:00
|
|
|
|
|
|
|
|
// Force node to send out the headers, this is required for the /container/wait api to make the docker cli proceed
|
|
|
|
|
res.write(' ');
|
|
|
|
|
|
|
|
|
|
dockerResponse.on('error', function (error) { console.error('dockerResponse error:', error); });
|
2018-08-13 21:10:53 +02:00
|
|
|
dockerResponse.pipe(res, { end: true });
|
|
|
|
|
});
|
|
|
|
|
|
2018-08-13 22:14:56 +02:00
|
|
|
req.on('error', function (error) { console.error('req error:', error); });
|
|
|
|
|
|
2018-08-14 22:52:00 +02:00
|
|
|
if (req.method === 'POST' && req.url.match(/\/containers\/create/)) {
|
|
|
|
|
gJSONParser(req, res, function () {
|
|
|
|
|
// overwrite the network the container lives in
|
|
|
|
|
req.body.HostConfig.NetworkMode = 'cloudron';
|
|
|
|
|
|
|
|
|
|
var plainBody = JSON.stringify(req.body);
|
|
|
|
|
|
|
|
|
|
dockerRequest.setHeader('Content-Length', Buffer.byteLength(plainBody));
|
|
|
|
|
dockerRequest.end(plainBody);
|
|
|
|
|
});
|
|
|
|
|
} else if (!req.readable) {
|
2018-08-13 21:10:53 +02:00
|
|
|
dockerRequest.end();
|
|
|
|
|
} else {
|
|
|
|
|
req.pipe(dockerRequest, { end: true });
|
|
|
|
|
}
|
|
|
|
|
}).listen(config.get('dockerProxyPort'), callback);
|
2018-08-13 22:14:56 +02:00
|
|
|
|
|
|
|
|
gServer.on('upgrade', function (req, client, head) {
|
|
|
|
|
// 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');
|
|
|
|
|
|
|
|
|
|
if (gServer) gServer.close();
|
|
|
|
|
|
|
|
|
|
callback();
|
|
|
|
|
}
|