Complete XenServer Secure Console proxy implementation

This commit is contained in:
Kelven Yang 2012-04-10 15:57:31 -07:00
parent f34c84c68f
commit f38ba2efbe
8 changed files with 103 additions and 41 deletions

View File

@ -365,14 +365,10 @@ public class ConsoleProxy {
} else if (!viewer.isFrontEndAlive()) {
s_logger.info("The rfb thread died, reinitializing the viewer " + viewer);
viewer.initClient(param);
reportLoadChange = true;
} else if (!param.getClientHostPassword().equals(viewer.getClientHostPassword())) {
s_logger.warn("Bad sid detected(VNC port may be reused). sid in session: " + viewer.getClientHostPassword()
+ ", sid in request: " + param.getClientHostPassword());
viewer.initClient(param);
reportLoadChange = true;
}
}
@ -404,12 +400,10 @@ public class ConsoleProxy {
s_logger.info("The rfb thread died, reinitializing the viewer " +
viewer);
viewer.initClient(param);
reportLoadChange = true;
} else if (!param.getClientHostPassword().equals(viewer.getClientHostPassword())) {
s_logger.warn("Bad sid detected(VNC port may be reused). sid in session: "
+ viewer.getClientHostPassword() + ", sid in request: " + param.getClientHostPassword());
viewer.initClient(param);
reportLoadChange = true;
} else {
if(ajaxSession == null || ajaxSession.isEmpty())
authenticationExternally(param);

View File

@ -71,6 +71,8 @@ public class ConsoleProxyAjaxHandler implements HttpHandler {
String ticket = queryMap.get("ticket");
String ajaxSessionIdStr = queryMap.get("sess");
String eventStr = queryMap.get("event");
String console_url = queryMap.get("consoleurl");
String console_host_session = queryMap.get("sessionref");
if(tag == null)
tag = "";
@ -116,6 +118,8 @@ public class ConsoleProxyAjaxHandler implements HttpHandler {
param.setClientHostPassword(sid);
param.setClientTag(tag);
param.setTicket(ticket);
param.setClientTunnelUrl(console_url);
param.setClientTunnelSession(console_host_session);
viewer = ConsoleProxy.getAjaxVncViewer(param, ajaxSessionIdStr);
} catch(Exception e) {
@ -184,6 +188,11 @@ public class ConsoleProxyAjaxHandler implements HttpHandler {
String name = param.split("=")[0];
String value = param.split("=")[1];
map.put(name, value);
} else if (paramTokens.length == 3) {
// very ugly, added for Xen tunneling url
String name = paramTokens[0];
String value = paramTokens[1] + "=" + paramTokens[2];
map.put(name, value);
} else {
if(s_logger.isDebugEnabled())
s_logger.debug("Invalid paramemter in URL found. param: " + param);

View File

@ -62,8 +62,6 @@ public class ConsoleProxyAjaxImageHandler implements HttpHandler {
String tag = queryMap.get("tag");
String ticket = queryMap.get("ticket");
String keyStr = queryMap.get("key");
String console_url = queryMap.get("consoleurl");
String console_host_session = queryMap.get("sessionref");
int key = 0;
if(tag == null)

View File

@ -25,6 +25,9 @@ public class ConsoleProxyClientParam {
private String clientTag;
private String ticket;
private String clientTunnelUrl;
private String clientTunnelSession;
public ConsoleProxyClientParam() {
clientHostPort = 0;
}
@ -69,6 +72,22 @@ public class ConsoleProxyClientParam {
this.ticket = ticket;
}
public String getClientTunnelUrl() {
return clientTunnelUrl;
}
public void setClientTunnelUrl(String clientTunnelUrl) {
this.clientTunnelUrl = clientTunnelUrl;
}
public String getClientTunnelSession() {
return clientTunnelSession;
}
public void setClientTunnelSession(String clientTunnelSession) {
this.clientTunnelSession = clientTunnelSession;
}
public String getClientMapKey() {
return clientHostAddress + ":" + clientHostPort;
}

View File

@ -107,8 +107,11 @@ public class ConsoleProxyThumbnailHandler implements HttpHandler {
String sid = queryMap.get("sid");
String tag = queryMap.get("tag");
String ticket = queryMap.get("ticket");
String console_url = queryMap.get("consoleurl");
String console_host_session = queryMap.get("sessionref");
if(tag == null)
tag = "";
tag = "";
if (ws == null || hs == null || host == null || portStr == null || sid == null ) {
throw new IllegalArgumentException();

View File

@ -13,6 +13,8 @@
package com.cloud.consoleproxy;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.UnknownHostException;
import org.apache.log4j.Logger;
@ -57,15 +59,33 @@ public class ConsoleProxyVncClient extends ConsoleProxyClientBase {
this.port = param.getClientHostPort();
this.passwordParam = param.getClientHostPassword();
this.tag = param.getClientTag();
this.ticket = param.getTicket();
this.ticket = param.getTicket();
final String tunnelUrl = param.getClientTunnelUrl();
final String tunnelSession = param.getClientTunnelSession();
client = new VncClient(this);
worker = new Thread(new Runnable() {
public void run() {
long startTick = System.currentTimeMillis();
while(System.currentTimeMillis() - startTick < 7000) {
try {
client.connectTo(host, port, passwordParam);
try {
if(tunnelUrl != null && !tunnelUrl.isEmpty() && tunnelSession != null && !tunnelSession.isEmpty()) {
try {
URI uri = new URI(tunnelUrl);
s_logger.info("Connect to VNC server via tunnel. url: " + tunnelUrl + ", session: " + tunnelSession);
client.connectTo(
uri.getHost(), uri.getPort(),
uri.getPath() + "?" + uri.getQuery(),
tunnelSession, "https".equalsIgnoreCase(uri.getScheme()),
passwordParam);
} catch (URISyntaxException e) {
s_logger.warn("Invalid tunnel URL " + tunnelUrl);
}
} else {
s_logger.info("Connect to VNC server directly. host: " + host + ", port: " + port);
client.connectTo(host, port, passwordParam);
}
} catch (UnknownHostException e) {
s_logger.error("Unexpected exception: ", e);
} catch (IOException e) {

View File

@ -126,10 +126,17 @@ public class VncClient {
}
public void connectTo(String host, int port, String path,
String session, boolean useSSL) throws UnknownHostException, IOException {
String session, boolean useSSL, String sid) throws UnknownHostException, IOException {
if(port < 0) {
if(useSSL)
port = 443;
else
port = 80;
}
RawHTTP tunnel = new RawHTTP("CONNECT", host, port, path, session, useSSL);
this.socket = tunnel.connect();
doConnect("");
doConnect(sid);
}
public void connectTo(String host, int port, String password) throws UnknownHostException, IOException {

View File

@ -42,6 +42,7 @@ import com.cloud.user.DomainManager;
import com.cloud.user.User;
import com.cloud.uservm.UserVm;
import com.cloud.utils.Pair;
import com.cloud.utils.Ternary;
import com.cloud.utils.component.ComponentLocator;
import com.cloud.utils.db.Transaction;
import com.cloud.vm.VMInstanceVO;
@ -283,26 +284,50 @@ public class ConsoleProxyServlet extends HttpServlet {
sendResponse(resp, "success");
}
// put the ugly stuff here
static private Ternary<String, String, String> parseHostInfo(String hostInfo) {
String host = null;
String tunnelUrl = null;
String tunnelSession = null;
if(hostInfo != null && hostInfo.startsWith("consoleurl")) {
String tokens[] = hostInfo.split("&");
host = hostInfo.substring(19, hostInfo.indexOf('/', 19)).trim();
tunnelUrl = tokens[0].substring("consoleurl=".length());
tunnelSession = tokens[1].split("=")[1];
} else {
host = hostInfo;
}
return new Ternary<String, String, String>(host, tunnelUrl, tunnelSession);
}
private String composeThumbnailUrl(String rootUrl, VMInstanceVO vm, HostVO hostVo, int w, int h) {
StringBuffer sb = new StringBuffer(rootUrl);
String host = hostVo.getPrivateIpAddress();
Pair<String, Integer> portInfo = _ms.getVncPort(vm);
if(portInfo.first() != null) {
host = portInfo.first();
}
Ternary<String, String, String> parsedHostInfo = parseHostInfo(portInfo.first());
String sid = vm.getVncPassword();
String tag = String.valueOf(vm.getId());
tag = _identityService.getIdentityUuid("vm_instance", tag);
String ticket = genAccessTicket(host, String.valueOf(portInfo.second()), sid, tag);
sb.append("/getscreen?host=").append(host);
sb.append("/getscreen?host=").append(parsedHostInfo.first());
sb.append("&port=").append(portInfo.second());
sb.append("&sid=").append(sid);
sb.append("&w=").append(w).append("&h=").append(h);
sb.append("&tag=").append(tag);
sb.append("&ticket=").append(ticket);
if(parsedHostInfo.second() != null && parsedHostInfo.third() != null) {
sb.append("&").append("consoleurl=").append(URLEncoder.encode(parsedHostInfo.second()));
sb.append("&").append("sessionref=").append(URLEncoder.encode(parsedHostInfo.third()));
}
if(s_logger.isDebugEnabled()) {
s_logger.debug("Compose thumbnail url: " + sb.toString());
}
@ -311,41 +336,28 @@ public class ConsoleProxyServlet extends HttpServlet {
private String composeConsoleAccessUrl(String rootUrl, VMInstanceVO vm, HostVO hostVo) {
StringBuffer sb = new StringBuffer(rootUrl);
String[] console_session = null;
String console_url = null;
String host = hostVo.getPrivateIpAddress();
Pair<String, Integer> portInfo = _ms.getVncPort(vm);
s_logger.debug("Port info " + portInfo.first());
if(portInfo.first() != null) {
console_url = host = portInfo.first();
}
System.out.println("Port info " + portInfo.first());
if ( console_url !=null && console_url.startsWith("consoleurl")) {
console_session = console_url.split("&");
host = console_url.substring(19,console_url.indexOf('/', 19)).trim();
}
if(s_logger.isDebugEnabled())
s_logger.debug("Port info " + portInfo.first());
Ternary<String, String, String> parsedHostInfo = parseHostInfo(portInfo.first());
String sid = vm.getVncPassword();
String tag = String.valueOf(vm.getId());
tag = _identityService.getIdentityUuid("vm_instance", tag);
String ticket = genAccessTicket(host, String.valueOf(portInfo.second()), sid, tag);
sb.append("/ajax?host=").append(host);
sb.append("/ajax?host=").append(parsedHostInfo.first());
sb.append("&port=").append(portInfo.second());
sb.append("&sid=").append(sid);
sb.append("&tag=").append(tag);
sb.append("&ticket=").append(ticket);
System.out.println("Port info " + portInfo.first());
if ( console_session !=null && console_session.length == 2){
sb.append("&").append(console_session[0]);
sb.append("&").append(console_session[1]);
if(parsedHostInfo.second() != null && parsedHostInfo.third() != null) {
sb.append("&").append("consoleurl=").append(URLEncoder.encode(parsedHostInfo.second()));
sb.append("&").append("sessionref=").append(URLEncoder.encode(parsedHostInfo.third()));
}
// for console access, we need guest OS type to help implement keyboard