mirror of https://github.com/apache/cloudstack.git
Support console access through rebooting with XAPI session re-negotiation
This commit is contained in:
parent
297996e907
commit
e8a5d51da7
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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())
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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<ClientPacket> queue = new ArrayBlockingQueue<ClientPacket>(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();
|
||||
|
|
|
|||
|
|
@ -83,7 +83,7 @@ public class VncServerPacketReceiver implements Runnable {
|
|||
}
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
|
||||
s_logger.error("Unexpected exception: ", e);
|
||||
if (connectionAlive) {
|
||||
closeConnection();
|
||||
vncConnection.shutdown();
|
||||
|
|
|
|||
|
|
@ -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<String, String, String> 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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<String, String, String> parseHostInfo(String hostInfo) {
|
||||
static public Ternary<String, String, String> 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;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue