Console proxy (fix vatesfr/xo#14).
This commit is contained in:
@@ -21,6 +21,7 @@ $serveStatic = require 'serve-static'
|
||||
$API = require './api'
|
||||
$Connection = require './connection'
|
||||
$WebServer = require 'http-server-plus'
|
||||
$wsProxy = require './ws-proxy'
|
||||
$XO = require './xo'
|
||||
{$coroutine, $fiberize, $waitEvent, $wait} = require './fibers-utils'
|
||||
{$fileExists, $wrap} = require './utils'
|
||||
@@ -45,6 +46,42 @@ $httpListenFailure = (error) ->
|
||||
|
||||
#=====================================================================
|
||||
|
||||
getVmConsoleUrl = (xo, id) ->
|
||||
vm = xo.getObject(id, 'VM')
|
||||
return unless vm?.power_state is 'Running'
|
||||
|
||||
{sessionId} = xo.getXAPI(vm)
|
||||
|
||||
for console in vm.consoles
|
||||
if console.protocol is 'rfb'
|
||||
return "#{console.location}&session_id=#{sessionId}"
|
||||
|
||||
return
|
||||
|
||||
CONSOLE_PROXY_PATH_RE = /^\/consoles\/(.*)$/
|
||||
setUpConsoleProxy = (webServer, xo) ->
|
||||
webSocketServer = new $WSServer({
|
||||
noServer: true,
|
||||
})
|
||||
|
||||
webServer.on('upgrade', (req, res, head) ->
|
||||
matches = CONSOLE_PROXY_PATH_RE.exec(req.url)
|
||||
return unless matches
|
||||
|
||||
url = getVmConsoleUrl(xo, matches[1])
|
||||
return unless url
|
||||
|
||||
webSocketServer.handleUpgrade(req, res, head, (connection) ->
|
||||
$wsProxy(connection, url)
|
||||
return
|
||||
)
|
||||
return
|
||||
)
|
||||
|
||||
return
|
||||
|
||||
#=====================================================================
|
||||
|
||||
# Main.
|
||||
exports = module.exports = $coroutine (args) ->
|
||||
return exports.help() unless (
|
||||
@@ -128,6 +165,9 @@ exports = module.exports = $coroutine (args) ->
|
||||
|
||||
connect.use $bind xo.handleProxyRequest, xo
|
||||
|
||||
# WebSocket server for consoles.
|
||||
setUpConsoleProxy(webServer, xo)
|
||||
|
||||
# Create the WebSocket server.
|
||||
wsServer = new $WSServer {
|
||||
server: webServer
|
||||
|
||||
@@ -328,9 +328,6 @@ module.exports = ->
|
||||
rule: 'VM'
|
||||
bind: -> @genval.$poolRef
|
||||
}
|
||||
|
||||
# FIXME: Should be remove ASAP!
|
||||
$sessionId : -> @genval.$sessionId ? @val.$sessionId
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
|
||||
51
src/ws-proxy.js
Normal file
51
src/ws-proxy.js
Normal file
@@ -0,0 +1,51 @@
|
||||
import assign from 'lodash.assign';
|
||||
import debug from 'debug';
|
||||
import WebSocket from 'ws';
|
||||
|
||||
debug = debug('xo:wsProxy');
|
||||
|
||||
let defaults = {
|
||||
// Automatically close the client connection when the remote close.
|
||||
autoClose: true,
|
||||
|
||||
// Reject secure connections to unauthorized remotes (bad CA).
|
||||
rejectUnauthorized: false,
|
||||
};
|
||||
|
||||
// Proxy a WebSocket `client` to a remote server which has `url` as
|
||||
// address.
|
||||
export default function wsProxy(client, url, opts) {
|
||||
opts = assign({}, defaults, opts);
|
||||
|
||||
let remote = new WebSocket(url, {
|
||||
protocol: opts.protocol || client.protocol,
|
||||
rejectUnauthorized: opts.rejectUnauthorized,
|
||||
}).once('open', function () {
|
||||
debug('connected to', url);
|
||||
}).once('close', function () {
|
||||
debug('remote closed');
|
||||
|
||||
if (opts.autoClose) {
|
||||
client.close();
|
||||
}
|
||||
}).once('error', function (error) {
|
||||
debug('remote error', error);
|
||||
}).on('message', function (message) {
|
||||
client.send(message, function (error) {
|
||||
if (error) {
|
||||
debug('client send error', error);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
client.once('close', function () {
|
||||
debug('client closed');
|
||||
remote.close();
|
||||
}).on('message', function (message) {
|
||||
remote.send(message, function (error) {
|
||||
if (error) {
|
||||
debug('remote send error', error);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -285,9 +285,6 @@ class $XO extends $EventEmitter
|
||||
# Normalizes the records.
|
||||
normalizeObject pool, ref, 'pool'
|
||||
|
||||
# FIXME: Remove this security flaw (currently necessary for consoles).
|
||||
pool.$sessionId = xapi.sessionId
|
||||
|
||||
objects[ref] = pool
|
||||
|
||||
# Then retrieve all other objects.
|
||||
@@ -335,10 +332,6 @@ class $XO extends $EventEmitter
|
||||
# Normalizes the object.
|
||||
normalizeObject object, ref, type
|
||||
|
||||
# FIXME: Remove this security flaw (currently necessary
|
||||
# for consoles).
|
||||
object.$sessionId = xapi.sessionId if type is 'pool'
|
||||
|
||||
# Adds the object to the corresponding list (and ensures
|
||||
# it is not in the other).
|
||||
if operation is 'del'
|
||||
|
||||
Reference in New Issue
Block a user