diff --git a/agent/src/com/cloud/agent/resource/consoleproxy/ConsoleProxyAuthenticationResult.java b/agent/src/com/cloud/agent/resource/consoleproxy/ConsoleProxyAuthenticationResult.java new file mode 100644 index 00000000000..ac58499bd58 --- /dev/null +++ b/agent/src/com/cloud/agent/resource/consoleproxy/ConsoleProxyAuthenticationResult.java @@ -0,0 +1,64 @@ +package com.cloud.agent.resource.consoleproxy; + +public class ConsoleProxyAuthenticationResult { + private boolean success; + private boolean isReauthentication; + private String host; + private int port; + private String tunnelUrl; + private String tunnelSession; + + public ConsoleProxyAuthenticationResult() { + success = false; + isReauthentication = false; + port = 0; + } + + public boolean isSuccess() { + return success; + } + + public void setSuccess(boolean success) { + this.success = success; + } + + public boolean isReauthentication() { + return isReauthentication; + } + + public void setReauthentication(boolean isReauthentication) { + this.isReauthentication = isReauthentication; + } + + public String getHost() { + return host; + } + + public void setHost(String host) { + this.host = host; + } + + public int getPort() { + return port; + } + + public void setPort(int port) { + this.port = port; + } + + public String getTunnelUrl() { + return tunnelUrl; + } + + public void setTunnelUrl(String tunnelUrl) { + this.tunnelUrl = tunnelUrl; + } + + public String getTunnelSession() { + return tunnelSession; + } + + public void setTunnelSession(String tunnelSession) { + this.tunnelSession = tunnelSession; + } +} diff --git a/agent/src/com/cloud/agent/resource/consoleproxy/ConsoleProxyResource.java b/agent/src/com/cloud/agent/resource/consoleproxy/ConsoleProxyResource.java index a0c9b2257c3..cc19d68e336 100644 --- a/agent/src/com/cloud/agent/resource/consoleproxy/ConsoleProxyResource.java +++ b/agent/src/com/cloud/agent/resource/consoleproxy/ConsoleProxyResource.java @@ -55,6 +55,7 @@ import com.cloud.resource.ServerResourceBase; import com.cloud.utils.NumbersUtil; import com.cloud.utils.net.NetUtils; import com.cloud.utils.script.Script; +import com.google.gson.Gson; /** * @@ -417,28 +418,36 @@ public class ConsoleProxyResource extends ServerResourceBase implements } } - public boolean authenticateConsoleAccess(String host, String port, - String vmId, String sid, String ticket) { + public String authenticateConsoleAccess(String host, String port, + String vmId, String sid, String ticket, Boolean isReauthentication) { + ConsoleAccessAuthenticationCommand cmd = new ConsoleAccessAuthenticationCommand( host, port, vmId, sid, ticket); + cmd.setReauthenticating(isReauthentication); + + ConsoleProxyAuthenticationResult result = new ConsoleProxyAuthenticationResult(); + result.setSuccess(false); + result.setReauthentication(isReauthentication); try { - AgentControlAnswer answer = getAgentControl().sendRequest(cmd, - 10000); + AgentControlAnswer answer = getAgentControl().sendRequest(cmd, 10000); + if (answer != null) { - return ((ConsoleAccessAuthenticationAnswer) answer).succeeded(); + ConsoleAccessAuthenticationAnswer authAnswer = (ConsoleAccessAuthenticationAnswer)answer; + result.setSuccess(authAnswer.succeeded()); + result.setHost(authAnswer.getHost()); + result.setPort(authAnswer.getPort()); + result.setTunnelUrl(authAnswer.getTunnelUrl()); + result.setTunnelSession(authAnswer.getTunnelSession()); } else { - s_logger.error("Authentication failed for vm: " + vmId - + " with sid: " + sid); + s_logger.error("Authentication failed for vm: " + vmId + " with sid: " + sid); } - } catch (AgentControlChannelException e) { - s_logger.error( - "Unable to send out console access authentication request due to " - + e.getMessage(), e); + s_logger.error("Unable to send out console access authentication request due to " + + e.getMessage(), e); } - return false; + return new Gson().toJson(result); } public void reportLoadInfo(String gsonLoadInfo) { diff --git a/api/src/com/cloud/agent/api/ConsoleAccessAuthenticationAnswer.java b/api/src/com/cloud/agent/api/ConsoleAccessAuthenticationAnswer.java index d16cbfb3224..c3801ef588d 100644 --- a/api/src/com/cloud/agent/api/ConsoleAccessAuthenticationAnswer.java +++ b/api/src/com/cloud/agent/api/ConsoleAccessAuthenticationAnswer.java @@ -10,21 +10,75 @@ // limitations under the License. // // Automatically generated by addcopyright.py at 04/03/2012 -package com.cloud.agent.api; - -public class ConsoleAccessAuthenticationAnswer extends AgentControlAnswer { - +package com.cloud.agent.api; + +public class ConsoleAccessAuthenticationAnswer extends AgentControlAnswer { + private boolean _success; + private boolean _isReauthenticating; + private String _host; + private int _port; + + private String _tunnelUrl; + private String _tunnelSession; + public ConsoleAccessAuthenticationAnswer() { + _success = false; + _isReauthenticating = false; + _port = 0; + } + + public ConsoleAccessAuthenticationAnswer(Command cmd, boolean success) { + super(cmd); + _success = success; + } + + public boolean succeeded() { + return _success; } - public ConsoleAccessAuthenticationAnswer(Command cmd, boolean success) { - super(cmd); - _success = success; + public void setSuccess(boolean value) { + _success = value; } - public boolean succeeded() { - return _success; + public boolean isReauthenticating() { + return _isReauthenticating; } -} + + public void setReauthenticating(boolean value) { + _isReauthenticating = value; + } + + public String getHost() { + return _host; + } + + public void setHost(String host) { + _host = host; + } + + public int getPort() { + return _port; + } + + public void setPort(int port) { + _port = port; + } + + public String getTunnelUrl() { + return _tunnelUrl; + } + + public void setTunnelUrl(String tunnelUrl) { + _tunnelUrl = tunnelUrl; + } + + public String getTunnelSession() { + return _tunnelSession; + } + + public void setTunnelSession(String tunnelSession) { + _tunnelSession = tunnelSession; + } +} diff --git a/api/src/com/cloud/agent/api/ConsoleAccessAuthenticationCommand.java b/api/src/com/cloud/agent/api/ConsoleAccessAuthenticationCommand.java index 03e158d867f..95f86691330 100644 --- a/api/src/com/cloud/agent/api/ConsoleAccessAuthenticationCommand.java +++ b/api/src/com/cloud/agent/api/ConsoleAccessAuthenticationCommand.java @@ -20,9 +20,12 @@ public class ConsoleAccessAuthenticationCommand extends AgentControlCommand { private String _sid; private String _ticket; + private boolean _isReauthenticating; + public ConsoleAccessAuthenticationCommand() { - } - + _isReauthenticating = false; + } + public ConsoleAccessAuthenticationCommand(String host, String port, String vmId, String sid, String ticket) { _host = host; _port = port; @@ -50,4 +53,12 @@ public class ConsoleAccessAuthenticationCommand extends AgentControlCommand { public String getTicket() { return _ticket; } -} + + public boolean isReauthenticating() { + return _isReauthenticating; + } + + public void setReauthenticating(boolean value) { + _isReauthenticating = value; + } +} diff --git a/console-proxy/src/com/cloud/consoleproxy/ConsoleProxy.java b/console-proxy/src/com/cloud/consoleproxy/ConsoleProxy.java index 96c57e40ba7..712d307b147 100644 --- a/console-proxy/src/com/cloud/consoleproxy/ConsoleProxy.java +++ b/console-proxy/src/com/cloud/consoleproxy/ConsoleProxy.java @@ -30,6 +30,7 @@ import org.apache.axis.encoding.Base64; import org.apache.log4j.xml.DOMConfigurator; import com.cloud.consoleproxy.util.Logger; +import com.google.gson.Gson; import com.sun.net.httpserver.HttpServer; /** @@ -171,9 +172,17 @@ public class ConsoleProxy { } } - public static boolean authenticateConsoleAccess(ConsoleProxyClientParam param) { - if(standaloneStart) - return true; + public static ConsoleProxyAuthenticationResult authenticateConsoleAccess(ConsoleProxyClientParam param, boolean reauthentication) { + + ConsoleProxyAuthenticationResult authResult = new ConsoleProxyAuthenticationResult(); + authResult.setSuccess(true); + authResult.setReauthentication(reauthentication); + authResult.setHost(param.getClientHostAddress()); + authResult.setPort(param.getClientHostPort()); + + if(standaloneStart) { + return authResult; + } if(authMethod != null) { Object result; @@ -183,26 +192,29 @@ public class ConsoleProxy { String.valueOf(param.getClientHostPort()), param.getClientTag(), param.getClientHostPassword(), - param.getTicket()); + param.getTicket(), + new Boolean(reauthentication)); } catch (IllegalAccessException e) { s_logger.error("Unable to invoke authenticateConsoleAccess due to IllegalAccessException" + " for vm: " + param.getClientTag(), e); - return false; + authResult.setSuccess(false); + return authResult; } catch (InvocationTargetException e) { s_logger.error("Unable to invoke authenticateConsoleAccess due to InvocationTargetException " + " for vm: " + param.getClientTag(), e); - return false; + authResult.setSuccess(false); + return authResult; } - if(result != null && result instanceof Boolean) { - return ((Boolean)result).booleanValue(); + if(result != null && result instanceof String) { + authResult = new Gson().fromJson((String)result, ConsoleProxyAuthenticationResult.class); } else { s_logger.error("Invalid authentication return object " + result + " for vm: " + param.getClientTag() + ", decline the access"); - return false; + authResult.setSuccess(false); } - } else { s_logger.warn("Private channel towards management server is not setup. Switch to offline mode and allow access to vm: " + param.getClientTag()); - return true; - } + } + + return authResult; } public static void reportLoadInfo(String gsonLoadInfo) { @@ -250,7 +262,7 @@ public class ConsoleProxy { ConsoleProxy.ksPassword = ksPassword; try { Class contextClazz = Class.forName("com.cloud.agent.resource.consoleproxy.ConsoleProxyResource"); - authMethod = contextClazz.getDeclaredMethod("authenticateConsoleAccess", String.class, String.class, String.class, String.class, String.class); + authMethod = contextClazz.getDeclaredMethod("authenticateConsoleAccess", String.class, String.class, String.class, String.class, String.class, Boolean.class); reportMethod = contextClazz.getDeclaredMethod("reportLoadInfo", String.class); ensureRouteMethod = contextClazz.getDeclaredMethod("ensureRoute", String.class); } catch (SecurityException e) { @@ -454,14 +466,20 @@ public class ConsoleProxy { return new ConsoleProxyClientStatsCollector(connectionMap); } - public static void authenticationExternally(ConsoleProxyClientParam param) throws AuthenticationException { - if(!authenticateConsoleAccess(param)) { + public static void authenticationExternally(ConsoleProxyClientParam param) throws AuthenticationException { + ConsoleProxyAuthenticationResult authResult = authenticateConsoleAccess(param, false); + + if(authResult == null || !authResult.isSuccess()) { s_logger.warn("External authenticator failed authencation request for vm " + param.getClientTag() + " with sid " + param.getClientHostPassword()); throw new AuthenticationException("External authenticator failed request for vm " + param.getClientTag() + " with sid " + param.getClientHostPassword()); } } + public static ConsoleProxyAuthenticationResult reAuthenticationExternally(ConsoleProxyClientParam param) { + return authenticateConsoleAccess(param, true); + } + public static String getEncryptorPassword() { return encryptorPassword; } diff --git a/console-proxy/src/com/cloud/consoleproxy/ConsoleProxyAjaxHandler.java b/console-proxy/src/com/cloud/consoleproxy/ConsoleProxyAjaxHandler.java index a4542db6a6e..f5dae7710b7 100644 --- a/console-proxy/src/com/cloud/consoleproxy/ConsoleProxyAjaxHandler.java +++ b/console-proxy/src/com/cloud/consoleproxy/ConsoleProxyAjaxHandler.java @@ -82,7 +82,7 @@ public class ConsoleProxyAjaxHandler implements HttpHandler { int port; - if(host == null || portStr == null || sid == null) + if(host == null || portStr == null || sid == null) throw new IllegalArgumentException(); try { @@ -159,8 +159,7 @@ public class ConsoleProxyAjaxHandler implements HttpHandler { } } else { if(ajaxSessionId != 0 && ajaxSessionId != viewer.getAjaxSessionId()) { - if(s_logger.isDebugEnabled()) - s_logger.debug("Ajax request comes from a different session, id in request: " + ajaxSessionId + ", id in viewer: " + viewer.getAjaxSessionId()); + s_logger.info("Ajax request comes from a different session, id in request: " + ajaxSessionId + ", id in viewer: " + viewer.getAjaxSessionId()); handleClientKickoff(t, viewer); } else if(ajaxSessionId == 0) { if(s_logger.isDebugEnabled()) diff --git a/console-proxy/src/com/cloud/consoleproxy/ConsoleProxyAuthenticationResult.java b/console-proxy/src/com/cloud/consoleproxy/ConsoleProxyAuthenticationResult.java new file mode 100644 index 00000000000..a86ed0bd3c2 --- /dev/null +++ b/console-proxy/src/com/cloud/consoleproxy/ConsoleProxyAuthenticationResult.java @@ -0,0 +1,65 @@ +package com.cloud.consoleproxy; + +// duplicated class +public class ConsoleProxyAuthenticationResult { + private boolean success; + private boolean isReauthentication; + private String host; + private int port; + private String tunnelUrl; + private String tunnelSession; + + public ConsoleProxyAuthenticationResult() { + success = false; + isReauthentication = false; + port = 0; + } + + public boolean isSuccess() { + return success; + } + + public void setSuccess(boolean success) { + this.success = success; + } + + public boolean isReauthentication() { + return isReauthentication; + } + + public void setReauthentication(boolean isReauthentication) { + this.isReauthentication = isReauthentication; + } + + public String getHost() { + return host; + } + + public void setHost(String host) { + this.host = host; + } + + public int getPort() { + return port; + } + + public void setPort(int port) { + this.port = port; + } + + public String getTunnelUrl() { + return tunnelUrl; + } + + public void setTunnelUrl(String tunnelUrl) { + this.tunnelUrl = tunnelUrl; + } + + public String getTunnelSession() { + return tunnelSession; + } + + public void setTunnelSession(String tunnelSession) { + this.tunnelSession = tunnelSession; + } +} diff --git a/console-proxy/src/com/cloud/consoleproxy/ConsoleProxyVncClient.java b/console-proxy/src/com/cloud/consoleproxy/ConsoleProxyVncClient.java index 09895f0831e..729302559dd 100644 --- a/console-proxy/src/com/cloud/consoleproxy/ConsoleProxyVncClient.java +++ b/console-proxy/src/com/cloud/consoleproxy/ConsoleProxyVncClient.java @@ -48,8 +48,10 @@ public class ConsoleProxyVncClient extends ConsoleProxyClientBase { @Override public boolean isFrontEndAlive() { - if(workerDone || System.currentTimeMillis() - getClientLastFrontEndActivityTime() > ConsoleProxy.VIEWER_LINGER_SECONDS*1000) - return false; + if(workerDone || System.currentTimeMillis() - getClientLastFrontEndActivityTime() > ConsoleProxy.VIEWER_LINGER_SECONDS*1000) { + s_logger.info("Front end has been idle for too long"); + return false; + } return true; } @@ -57,34 +59,28 @@ public class ConsoleProxyVncClient extends ConsoleProxyClientBase { public void initClient(ConsoleProxyClientParam param) { setClientParam(param); - 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) { + String tunnelUrl = getClientParam().getClientTunnelUrl(); + String tunnelSession = getClientParam().getClientTunnelSession(); + + for(int i = 0; i < 15; i++) { 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()), - getClientHostPassword()); - } catch (URISyntaxException e) { - s_logger.warn("Invalid tunnel URL " + tunnelUrl); - } + 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()), + getClientHostPassword()); } else { s_logger.info("Connect to VNC server directly. host: " + getClientHostAddress() + ", port: " + getClientHostPort()); client.connectTo(getClientHostAddress(), getClientHostPort(), getClientHostPassword()); } } catch (UnknownHostException e) { - s_logger.error("Unexpected exception", e); - break; + s_logger.error("Unexpected exception (will retry until timeout)", e); } catch (IOException e) { s_logger.error("Unexpected exception (will retry until timeout) ", e); } catch (Throwable e) { @@ -94,11 +90,26 @@ public class ConsoleProxyVncClient extends ConsoleProxyClientBase { try { Thread.sleep(1000); } catch (InterruptedException e) { - } + } + + if(tunnelUrl != null && !tunnelUrl.isEmpty() && tunnelSession != null && !tunnelSession.isEmpty()) { + ConsoleProxyAuthenticationResult authResult = ConsoleProxy.reAuthenticationExternally(getClientParam()); + if(authResult != null && authResult.isSuccess()) { + if(authResult.getTunnelUrl() != null && !authResult.getTunnelUrl().isEmpty() && + authResult.getTunnelSession() != null && !authResult.getTunnelSession().isEmpty()) { + tunnelUrl = authResult.getTunnelUrl(); + tunnelSession = authResult.getTunnelSession(); + + s_logger.info("Reset XAPI session. url: " + tunnelUrl + ", session: " + tunnelSession); + } + } + } } - - workerDone = true; - } + + s_logger.info("Receiver thread stopped."); + workerDone = true; + client.getClientListener().onClientClose(); + } }); worker.setDaemon(true); @@ -115,7 +126,9 @@ public class ConsoleProxyVncClient extends ConsoleProxyClientBase { public void onClientConnected() { } - public void onClientClose() { + public void onClientClose() { + s_logger.info("Received client close indication. remove viewer from map."); + ConsoleProxy.removeViewer(this); } diff --git a/console-proxy/src/com/cloud/consoleproxy/vnc/VncClient.java b/console-proxy/src/com/cloud/consoleproxy/vnc/VncClient.java index 954480648e8..8e1ff4d8a51 100644 --- a/console-proxy/src/com/cloud/consoleproxy/vnc/VncClient.java +++ b/console-proxy/src/com/cloud/consoleproxy/vnc/VncClient.java @@ -121,10 +121,12 @@ public class VncClient { } catch (Throwable e) { } } - - clientListener.onClientClose(); } + public ConsoleProxyClientListener getClientListener() { + return clientListener; + } + public void connectTo(String host, int port, String path, String session, boolean useSSL, String sid) throws UnknownHostException, IOException { if(port < 0) { diff --git a/console-proxy/src/com/cloud/consoleproxy/vnc/VncClientPacketSender.java b/console-proxy/src/com/cloud/consoleproxy/vnc/VncClientPacketSender.java index 671cbd51e70..42955c1d537 100644 --- a/console-proxy/src/com/cloud/consoleproxy/vnc/VncClientPacketSender.java +++ b/console-proxy/src/com/cloud/consoleproxy/vnc/VncClientPacketSender.java @@ -22,6 +22,7 @@ import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.BlockingQueue; import java.util.concurrent.TimeUnit; +import com.cloud.consoleproxy.util.Logger; import com.cloud.consoleproxy.vnc.packet.client.ClientPacket; import com.cloud.consoleproxy.vnc.packet.client.FramebufferUpdateRequestPacket; import com.cloud.consoleproxy.vnc.packet.client.KeyboardEventPacket; @@ -30,6 +31,7 @@ import com.cloud.consoleproxy.vnc.packet.client.SetEncodingsPacket; import com.cloud.consoleproxy.vnc.packet.client.SetPixelFormatPacket; public class VncClientPacketSender implements Runnable, PaintNotificationListener, KeyListener, MouseListener, MouseMotionListener, FrameBufferUpdateListener { + private static final Logger s_logger = Logger.getLogger(VncClientPacketSender.class); // Queue for outgoing packets private final BlockingQueue queue = new ArrayBlockingQueue(30); @@ -68,6 +70,7 @@ public class VncClientPacketSender implements Runnable, PaintNotificationListene } } } catch (Throwable e) { + s_logger.error("Unexpected exception: ", e); if (connectionAlive) { closeConnection(); vncConnection.shutdown(); diff --git a/console-proxy/src/com/cloud/consoleproxy/vnc/VncServerPacketReceiver.java b/console-proxy/src/com/cloud/consoleproxy/vnc/VncServerPacketReceiver.java index 757f5acaf82..d369182b5e1 100644 --- a/console-proxy/src/com/cloud/consoleproxy/vnc/VncServerPacketReceiver.java +++ b/console-proxy/src/com/cloud/consoleproxy/vnc/VncServerPacketReceiver.java @@ -83,7 +83,7 @@ public class VncServerPacketReceiver implements Runnable { } } } catch (Throwable e) { - + s_logger.error("Unexpected exception: ", e); if (connectionAlive) { closeConnection(); vncConnection.shutdown(); diff --git a/server/src/com/cloud/consoleproxy/ConsoleProxyManagerImpl.java b/server/src/com/cloud/consoleproxy/ConsoleProxyManagerImpl.java index 088be6296ce..9bd2d9cfeaa 100755 --- a/server/src/com/cloud/consoleproxy/ConsoleProxyManagerImpl.java +++ b/server/src/com/cloud/consoleproxy/ConsoleProxyManagerImpl.java @@ -33,6 +33,8 @@ import com.cloud.agent.api.Answer; import com.cloud.agent.api.ConsoleAccessAuthenticationAnswer; import com.cloud.agent.api.ConsoleAccessAuthenticationCommand; import com.cloud.agent.api.ConsoleProxyLoadReportCommand; +import com.cloud.agent.api.GetVncPortAnswer; +import com.cloud.agent.api.GetVncPortCommand; import com.cloud.agent.api.RebootCommand; import com.cloud.agent.api.StartupCommand; import com.cloud.agent.api.StartupProxyCommand; @@ -109,6 +111,7 @@ import com.cloud.user.User; import com.cloud.utils.DateUtil; import com.cloud.utils.NumbersUtil; import com.cloud.utils.Pair; +import com.cloud.utils.Ternary; import com.cloud.utils.component.Adapters; import com.cloud.utils.component.ComponentLocator; import com.cloud.utils.component.Inject; @@ -865,25 +868,27 @@ public class ConsoleProxyManagerImpl implements ConsoleProxyManager, ConsoleProx s_logger.debug("Console authentication. Ticket in url for " + cmd.getHost() + ":" + cmd.getPort() + "-" + cmd.getVmId() + " is " + ticketInUrl); } - String ticket = ConsoleProxyServlet.genAccessTicket(cmd.getHost(), cmd.getPort(), cmd.getSid(), cmd.getVmId()); - if (s_logger.isDebugEnabled()) { - s_logger.debug("Console authentication. Ticket in 1 minute boundary for " + cmd.getHost() + ":" + cmd.getPort() + "-" + cmd.getVmId() + " is " + ticket); - } - - if (!ticket.equals(ticketInUrl)) { - Date now = new Date(); - // considering of minute round-up - String minuteEarlyTicket = ConsoleProxyServlet.genAccessTicket(cmd.getHost(), cmd.getPort(), cmd.getSid(), cmd.getVmId(), new Date(now.getTime() - 60 * 1000)); - - if (s_logger.isDebugEnabled()) { - s_logger.debug("Console authentication. Ticket in 2-minute boundary for " + cmd.getHost() + ":" + cmd.getPort() + "-" + cmd.getVmId() + " is " + minuteEarlyTicket); - } - - if (!minuteEarlyTicket.equals(ticketInUrl)) { - s_logger.error("Access ticket expired or has been modified. vmId: " + cmd.getVmId() + "ticket in URL: " + ticketInUrl + ", tickets to check against: " + ticket + "," - + minuteEarlyTicket); - return new ConsoleAccessAuthenticationAnswer(cmd, false); - } + if(!cmd.isReauthenticating()) { + String ticket = ConsoleProxyServlet.genAccessTicket(cmd.getHost(), cmd.getPort(), cmd.getSid(), cmd.getVmId()); + if (s_logger.isDebugEnabled()) { + s_logger.debug("Console authentication. Ticket in 1 minute boundary for " + cmd.getHost() + ":" + cmd.getPort() + "-" + cmd.getVmId() + " is " + ticket); + } + + if (!ticket.equals(ticketInUrl)) { + Date now = new Date(); + // considering of minute round-up + String minuteEarlyTicket = ConsoleProxyServlet.genAccessTicket(cmd.getHost(), cmd.getPort(), cmd.getSid(), cmd.getVmId(), new Date(now.getTime() - 60 * 1000)); + + if (s_logger.isDebugEnabled()) { + s_logger.debug("Console authentication. Ticket in 2-minute boundary for " + cmd.getHost() + ":" + cmd.getPort() + "-" + cmd.getVmId() + " is " + minuteEarlyTicket); + } + + if (!minuteEarlyTicket.equals(ticketInUrl)) { + s_logger.error("Access ticket expired or has been modified. vmId: " + cmd.getVmId() + "ticket in URL: " + ticketInUrl + ", tickets to check against: " + ticket + "," + + minuteEarlyTicket); + return new ConsoleAccessAuthenticationAnswer(cmd, false); + } + } } if (cmd.getVmId() != null && cmd.getVmId().isEmpty()) { @@ -899,10 +904,6 @@ public class ConsoleProxyManagerImpl implements ConsoleProxyManager, ConsoleProx return new ConsoleAccessAuthenticationAnswer(cmd, false); } - // TODO authentication channel between console proxy VM and management - // server needs to be secured, - // the data is now being sent through private network, but this is - // apparently not enough VMInstanceVO vm = _instanceDao.findById(vmId); if (vm == null) { return new ConsoleAccessAuthenticationAnswer(cmd, false); @@ -924,6 +925,40 @@ public class ConsoleProxyManagerImpl implements ConsoleProxyManager, ConsoleProx s_logger.warn("sid " + sid + " in url does not match stored sid " + vm.getVncPassword()); return new ConsoleAccessAuthenticationAnswer(cmd, false); } + + if(cmd.isReauthenticating()) { + ConsoleAccessAuthenticationAnswer authenticationAnswer = new ConsoleAccessAuthenticationAnswer(cmd, true); + authenticationAnswer.setReauthenticating(true); + + s_logger.info("Re-authentication request, ask host " + vm.getHostId() + " for new console info"); + GetVncPortAnswer answer = (GetVncPortAnswer) _agentMgr.easySend(vm.getHostId(), new + GetVncPortCommand(vm.getId(), vm.getInstanceName())); + + if (answer != null && answer.getResult()) { + Ternary parsedHostInfo = ConsoleProxyServlet.parseHostInfo(answer.getAddress()); + + if(parsedHostInfo.second() != null && parsedHostInfo.third() != null) { + + s_logger.info("Re-authentication result. vm: " + vm.getId() + ", tunnel url: " + parsedHostInfo.second() + + ", tunnel session: " + parsedHostInfo.third()); + + authenticationAnswer.setTunnelUrl(parsedHostInfo.second()); + authenticationAnswer.setTunnelSession(parsedHostInfo.third()); + } else { + s_logger.info("Re-authentication result. vm: " + vm.getId() + ", host address: " + parsedHostInfo.first() + + ", port: " + answer.getPort()); + + authenticationAnswer.setHost(parsedHostInfo.first()); + authenticationAnswer.setPort(answer.getPort()); + } + } else { + s_logger.warn("Re-authentication request failed"); + + authenticationAnswer.setSuccess(false); + } + + return authenticationAnswer; + } return new ConsoleAccessAuthenticationAnswer(cmd, true); } diff --git a/server/src/com/cloud/servlet/ConsoleProxyServlet.java b/server/src/com/cloud/servlet/ConsoleProxyServlet.java index d400d51ec60..31320d3f69a 100644 --- a/server/src/com/cloud/servlet/ConsoleProxyServlet.java +++ b/server/src/com/cloud/servlet/ConsoleProxyServlet.java @@ -13,8 +13,6 @@ package com.cloud.servlet; import java.io.IOException; -import java.io.UnsupportedEncodingException; -import java.net.URLDecoder; import java.net.URLEncoder; import java.util.ArrayList; import java.util.Collections; @@ -40,7 +38,6 @@ import com.cloud.server.ManagementServer; import com.cloud.storage.GuestOSVO; import com.cloud.user.Account; import com.cloud.user.AccountManager; -import com.cloud.user.DomainManager; import com.cloud.user.User; import com.cloud.uservm.UserVm; import com.cloud.utils.Pair; @@ -286,17 +283,23 @@ public class ConsoleProxyServlet extends HttpServlet { } // put the ugly stuff here - static private Ternary parseHostInfo(String hostInfo) { + static public Ternary parseHostInfo(String hostInfo) { String host = null; String tunnelUrl = null; String tunnelSession = null; + s_logger.info("Parse host info returned from executing GetVNCPortCommand. host info: " + hostInfo); + 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]; + if(hostInfo.length() > 19 && hostInfo.indexOf('/', 19) > 19) { + host = hostInfo.substring(19, hostInfo.indexOf('/', 19)).trim(); + tunnelUrl = tokens[0].substring("consoleurl=".length()); + tunnelSession = tokens[1].split("=")[1]; + } else { + host = ""; + } } else { host = hostInfo; }