From f38ba2efbe9abe85e3b4f6bb5b3a68bbed8d5ed5 Mon Sep 17 00:00:00 2001 From: Kelven Yang Date: Tue, 10 Apr 2012 15:57:31 -0700 Subject: [PATCH] Complete XenServer Secure Console proxy implementation --- .../com/cloud/consoleproxy/ConsoleProxy.java | 6 -- .../consoleproxy/ConsoleProxyAjaxHandler.java | 9 +++ .../ConsoleProxyAjaxImageHandler.java | 2 - .../consoleproxy/ConsoleProxyClientParam.java | 19 ++++++ .../ConsoleProxyThumbnailHandler.java | 5 +- .../consoleproxy/ConsoleProxyVncClient.java | 28 ++++++-- .../com/cloud/consoleproxy/vnc/VncClient.java | 11 +++- .../cloud/servlet/ConsoleProxyServlet.java | 64 +++++++++++-------- 8 files changed, 103 insertions(+), 41 deletions(-) diff --git a/console-proxy/src/com/cloud/consoleproxy/ConsoleProxy.java b/console-proxy/src/com/cloud/consoleproxy/ConsoleProxy.java index bad33a2b072..e12cfa0782b 100644 --- a/console-proxy/src/com/cloud/consoleproxy/ConsoleProxy.java +++ b/console-proxy/src/com/cloud/consoleproxy/ConsoleProxy.java @@ -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); diff --git a/console-proxy/src/com/cloud/consoleproxy/ConsoleProxyAjaxHandler.java b/console-proxy/src/com/cloud/consoleproxy/ConsoleProxyAjaxHandler.java index cef028591cf..8f44b7f777e 100644 --- a/console-proxy/src/com/cloud/consoleproxy/ConsoleProxyAjaxHandler.java +++ b/console-proxy/src/com/cloud/consoleproxy/ConsoleProxyAjaxHandler.java @@ -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); diff --git a/console-proxy/src/com/cloud/consoleproxy/ConsoleProxyAjaxImageHandler.java b/console-proxy/src/com/cloud/consoleproxy/ConsoleProxyAjaxImageHandler.java index e26a3ddb4a0..5aceb68c77f 100644 --- a/console-proxy/src/com/cloud/consoleproxy/ConsoleProxyAjaxImageHandler.java +++ b/console-proxy/src/com/cloud/consoleproxy/ConsoleProxyAjaxImageHandler.java @@ -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) diff --git a/console-proxy/src/com/cloud/consoleproxy/ConsoleProxyClientParam.java b/console-proxy/src/com/cloud/consoleproxy/ConsoleProxyClientParam.java index b4f9713c4f4..df688778070 100644 --- a/console-proxy/src/com/cloud/consoleproxy/ConsoleProxyClientParam.java +++ b/console-proxy/src/com/cloud/consoleproxy/ConsoleProxyClientParam.java @@ -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; } diff --git a/console-proxy/src/com/cloud/consoleproxy/ConsoleProxyThumbnailHandler.java b/console-proxy/src/com/cloud/consoleproxy/ConsoleProxyThumbnailHandler.java index 226df7b78ec..ef9dd5f94f4 100644 --- a/console-proxy/src/com/cloud/consoleproxy/ConsoleProxyThumbnailHandler.java +++ b/console-proxy/src/com/cloud/consoleproxy/ConsoleProxyThumbnailHandler.java @@ -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(); diff --git a/console-proxy/src/com/cloud/consoleproxy/ConsoleProxyVncClient.java b/console-proxy/src/com/cloud/consoleproxy/ConsoleProxyVncClient.java index 9e383fbe292..22dd7899935 100644 --- a/console-proxy/src/com/cloud/consoleproxy/ConsoleProxyVncClient.java +++ b/console-proxy/src/com/cloud/consoleproxy/ConsoleProxyVncClient.java @@ -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) { diff --git a/console-proxy/src/com/cloud/consoleproxy/vnc/VncClient.java b/console-proxy/src/com/cloud/consoleproxy/vnc/VncClient.java index b42df505f95..b7ed80c008c 100644 --- a/console-proxy/src/com/cloud/consoleproxy/vnc/VncClient.java +++ b/console-proxy/src/com/cloud/consoleproxy/vnc/VncClient.java @@ -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 { diff --git a/server/src/com/cloud/servlet/ConsoleProxyServlet.java b/server/src/com/cloud/servlet/ConsoleProxyServlet.java index fc8bcbf20af..007be540415 100644 --- a/server/src/com/cloud/servlet/ConsoleProxyServlet.java +++ b/server/src/com/cloud/servlet/ConsoleProxyServlet.java @@ -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 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(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 portInfo = _ms.getVncPort(vm); - if(portInfo.first() != null) { - host = portInfo.first(); - } + Ternary 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 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 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