-
Notifications
You must be signed in to change notification settings - Fork 1.3k
Backport: Honour consoleproxy.session.timeout for noVNC console sessions #13058
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: 4.20
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||
|---|---|---|---|---|---|---|---|---|
|
|
@@ -36,16 +36,15 @@ | |||||||
|
|
||||||||
| import org.apache.commons.lang3.ArrayUtils; | ||||||||
| import org.apache.commons.lang3.StringUtils; | ||||||||
| import org.apache.logging.log4j.LogManager; | ||||||||
| import org.apache.logging.log4j.Logger; | ||||||||
| import org.apache.logging.log4j.core.config.Configurator; | ||||||||
| import org.eclipse.jetty.websocket.api.Session; | ||||||||
|
|
||||||||
| import com.cloud.utils.PropertiesUtil; | ||||||||
| import com.google.gson.Gson; | ||||||||
| import com.sun.net.httpserver.HttpServer; | ||||||||
|
|
||||||||
| import org.apache.logging.log4j.LogManager; | ||||||||
| import org.apache.logging.log4j.Logger; | ||||||||
|
|
||||||||
| /** | ||||||||
| * | ||||||||
| * ConsoleProxy, singleton class that manages overall activities in console proxy process. To make legacy code work, we still | ||||||||
|
|
@@ -82,7 +81,7 @@ public class ConsoleProxy { | |||||||
| static boolean standaloneStart = false; | ||||||||
|
|
||||||||
| static String encryptorPassword = "Dummy"; | ||||||||
| static final String[] skipProperties = new String[]{"certificate", "cacertificate", "keystore_password", "privatekey"}; | ||||||||
| static final String[] skipProperties = new String[] {"certificate", "cacertificate", "keystore_password", "privatekey"}; | ||||||||
|
|
||||||||
| static Set<String> allowedSessions = new HashSet<>(); | ||||||||
|
|
||||||||
|
|
@@ -93,11 +92,13 @@ public static void addAllowedSession(String sessionUuid) { | |||||||
| private static void configLog4j() { | ||||||||
| final ClassLoader loader = Thread.currentThread().getContextClassLoader(); | ||||||||
| URL configUrl = loader.getResource("/conf/log4j-cloud.xml"); | ||||||||
| if (configUrl == null) | ||||||||
| if (configUrl == null) { | ||||||||
| configUrl = ClassLoader.getSystemResource("log4j-cloud.xml"); | ||||||||
| } | ||||||||
|
|
||||||||
| if (configUrl == null) | ||||||||
| if (configUrl == null) { | ||||||||
| configUrl = ClassLoader.getSystemResource("conf/log4j-cloud.xml"); | ||||||||
| } | ||||||||
|
|
||||||||
| if (configUrl != null) { | ||||||||
| try { | ||||||||
|
|
@@ -114,28 +115,28 @@ private static void configLog4j() { | |||||||
| } catch (URISyntaxException e) { | ||||||||
| System.out.println("Unable to convert log4j configuration Url to URI"); | ||||||||
| } | ||||||||
| // DOMConfigurator.configure(configUrl); | ||||||||
| } else { | ||||||||
| System.out.println("Configure log4j with default properties"); | ||||||||
| } | ||||||||
| } | ||||||||
|
|
||||||||
| private static void configProxy(Properties conf) { | ||||||||
| LOGGER.info("Configure console proxy..."); | ||||||||
| for (Object key : conf.keySet()) { | ||||||||
| LOGGER.info("Property " + (String)key + ": " + conf.getProperty((String)key)); | ||||||||
| if (!ArrayUtils.contains(skipProperties, key)) { | ||||||||
| LOGGER.info("Property " + (String)key + ": " + conf.getProperty((String)key)); | ||||||||
| if (conf != null) { | ||||||||
| for (Object key : conf.keySet()) { | ||||||||
| if (!ArrayUtils.contains(skipProperties, key)) { | ||||||||
| LOGGER.info("Property " + (String) key + ": " + conf.getProperty((String) key)); | ||||||||
| } | ||||||||
| } | ||||||||
| } | ||||||||
|
|
||||||||
| String s = conf.getProperty("consoleproxy.httpListenPort"); | ||||||||
| String s = conf != null ? conf.getProperty("consoleproxy.httpListenPort") : null; | ||||||||
| if (s != null) { | ||||||||
| httpListenPort = Integer.parseInt(s); | ||||||||
| LOGGER.info("Setting httpListenPort=" + s); | ||||||||
| } | ||||||||
|
|
||||||||
| s = conf.getProperty("premium"); | ||||||||
| s = conf != null ? conf.getProperty("premium") : null; | ||||||||
| if (s != null && s.equalsIgnoreCase("true")) { | ||||||||
| LOGGER.info("Premium setting will override settings from consoleproxy.properties, listen at port 443"); | ||||||||
| httpListenPort = 443; | ||||||||
|
|
@@ -144,25 +145,25 @@ private static void configProxy(Properties conf) { | |||||||
| factoryClzName = ConsoleProxyBaseServerFactoryImpl.class.getName(); | ||||||||
| } | ||||||||
|
|
||||||||
| s = conf.getProperty("consoleproxy.httpCmdListenPort"); | ||||||||
| s = conf != null ? conf.getProperty("consoleproxy.httpCmdListenPort") : null; | ||||||||
| if (s != null) { | ||||||||
| httpCmdListenPort = Integer.parseInt(s); | ||||||||
| LOGGER.info("Setting httpCmdListenPort=" + s); | ||||||||
| } | ||||||||
|
|
||||||||
| s = conf.getProperty("consoleproxy.reconnectMaxRetry"); | ||||||||
| s = conf != null ? conf.getProperty("consoleproxy.reconnectMaxRetry") : null; | ||||||||
| if (s != null) { | ||||||||
| reconnectMaxRetry = Integer.parseInt(s); | ||||||||
| LOGGER.info("Setting reconnectMaxRetry=" + reconnectMaxRetry); | ||||||||
| } | ||||||||
|
|
||||||||
| s = conf.getProperty("consoleproxy.readTimeoutSeconds"); | ||||||||
| s = conf != null ? conf.getProperty("consoleproxy.readTimeoutSeconds") : null; | ||||||||
| if (s != null) { | ||||||||
| readTimeoutSeconds = Integer.parseInt(s); | ||||||||
| LOGGER.info("Setting readTimeoutSeconds=" + readTimeoutSeconds); | ||||||||
| } | ||||||||
|
|
||||||||
| s = conf.getProperty("consoleproxy.defaultBufferSize"); | ||||||||
| s = conf != null ? conf.getProperty("consoleproxy.defaultBufferSize") : null; | ||||||||
| if (s != null) { | ||||||||
| defaultBufferSize = Integer.parseInt(s); | ||||||||
| LOGGER.info("Setting defaultBufferSize=" + defaultBufferSize); | ||||||||
|
|
@@ -173,7 +174,7 @@ public static ConsoleProxyServerFactory getHttpServerFactory() { | |||||||
| try { | ||||||||
| Class<?> clz = Class.forName(factoryClzName); | ||||||||
| try { | ||||||||
| ConsoleProxyServerFactory factory = (ConsoleProxyServerFactory)clz.newInstance(); | ||||||||
| ConsoleProxyServerFactory factory = (ConsoleProxyServerFactory) clz.newInstance(); | ||||||||
| factory.init(ConsoleProxy.ksBits, ConsoleProxy.ksPassword); | ||||||||
| return factory; | ||||||||
| } catch (InstantiationException e) { | ||||||||
|
|
@@ -197,11 +198,11 @@ public static ConsoleProxyAuthenticationResult authenticateConsoleAccess(Console | |||||||
| authResult.setHost(param.getClientHostAddress()); | ||||||||
| authResult.setPort(param.getClientHostPort()); | ||||||||
|
|
||||||||
| if (org.apache.commons.lang3.StringUtils.isNotBlank(param.getExtraSecurityToken())) { | ||||||||
| if (StringUtils.isNotBlank(param.getExtraSecurityToken())) { | ||||||||
| String extraToken = param.getExtraSecurityToken(); | ||||||||
| String clientProvidedToken = param.getClientProvidedExtraSecurityToken(); | ||||||||
| LOGGER.debug(String.format("Extra security validation for the console access, provided %s " + | ||||||||
| "to validate against %s", clientProvidedToken, extraToken)); | ||||||||
| LOGGER.debug(String.format("Extra security validation for the console access, provided %s to validate against %s", | ||||||||
| clientProvidedToken, extraToken)); | ||||||||
|
|
||||||||
| if (!extraToken.equals(clientProvidedToken)) { | ||||||||
| LOGGER.error("The provided extra token does not match the expected value for this console endpoint"); | ||||||||
|
|
@@ -233,20 +234,21 @@ public static ConsoleProxyAuthenticationResult authenticateConsoleAccess(Console | |||||||
| Object result; | ||||||||
| try { | ||||||||
| result = | ||||||||
| authMethod.invoke(ConsoleProxy.context, param.getClientHostAddress(), String.valueOf(param.getClientHostPort()), param.getClientTag(), | ||||||||
| param.getClientHostPassword(), param.getTicket(), reauthentication, param.getSessionUuid()); | ||||||||
| authMethod.invoke(ConsoleProxy.context, param.getClientHostAddress(), String.valueOf(param.getClientHostPort()), | ||||||||
| param.getClientTag(), param.getClientHostPassword(), param.getTicket(), reauthentication, | ||||||||
| param.getSessionUuid(), param.getClientIp()); | ||||||||
| } catch (IllegalAccessException e) { | ||||||||
| LOGGER.error("Unable to invoke authenticateConsoleAccess due to IllegalAccessException" + " for vm: " + param.getClientTag(), e); | ||||||||
| LOGGER.error("Unable to invoke authenticateConsoleAccess due to IllegalAccessException for vm: " + param.getClientTag(), e); | ||||||||
| authResult.setSuccess(false); | ||||||||
| return authResult; | ||||||||
| } catch (InvocationTargetException e) { | ||||||||
| LOGGER.error("Unable to invoke authenticateConsoleAccess due to InvocationTargetException " + " for vm: " + param.getClientTag(), e); | ||||||||
| LOGGER.error("Unable to invoke authenticateConsoleAccess due to InvocationTargetException for vm: " + param.getClientTag(), e); | ||||||||
| authResult.setSuccess(false); | ||||||||
| return authResult; | ||||||||
| } | ||||||||
|
|
||||||||
| if (result != null && result instanceof String) { | ||||||||
| authResult = new Gson().fromJson((String)result, ConsoleProxyAuthenticationResult.class); | ||||||||
| authResult = new Gson().fromJson((String) result, ConsoleProxyAuthenticationResult.class); | ||||||||
| } else { | ||||||||
| LOGGER.error("Invalid authentication return object " + result + " for vm: " + param.getClientTag() + ", decline the access"); | ||||||||
| authResult.setSuccess(false); | ||||||||
|
|
@@ -286,7 +288,8 @@ public static void ensureRoute(String address) { | |||||||
| } | ||||||||
| } | ||||||||
|
|
||||||||
| public static void startWithContext(Properties conf, Object context, byte[] ksBits, String ksPassword, String password, Boolean isSourceIpCheckEnabled) { | ||||||||
| public static void startWithContext(Properties conf, Object context, byte[] ksBits, String ksPassword, | ||||||||
| String password, Boolean isSourceIpCheckEnabled) { | ||||||||
| setEncryptorPassword(password); | ||||||||
| configLog4j(); | ||||||||
| LOGGER.info("Start console proxy with context"); | ||||||||
|
|
@@ -308,7 +311,7 @@ public static void startWithContext(Properties conf, Object context, byte[] ksBi | |||||||
| final ClassLoader loader = Thread.currentThread().getContextClassLoader(); | ||||||||
| Class<?> contextClazz = loader.loadClass("com.cloud.agent.resource.consoleproxy.ConsoleProxyResource"); | ||||||||
| authMethod = contextClazz.getDeclaredMethod("authenticateConsoleAccess", String.class, String.class, | ||||||||
| String.class, String.class, String.class, Boolean.class, String.class); | ||||||||
| String.class, String.class, String.class, Boolean.class, String.class, String.class); | ||||||||
|
||||||||
| String.class, String.class, String.class, Boolean.class, String.class, String.class); | |
| String.class, String.class, String.class, Boolean.class, String.class); |
Copilot
AI
Apr 28, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In getNoVncViewer(...), the AuthenticationException path logs "Authentication failed" without including the caught exception, which loses the stack trace/context needed to debug auth failures. Log the exception (and preferably include key identifiers like sessionUuid/clientKey) when catching the failure.
| LOGGER.error("Authentication failed for param: " + param); | |
| LOGGER.error("Authentication failed for sessionUuid={}, clientKey={}, param={}", | |
| param.getSessionUuid(), clientKey, param, e); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
authenticateConsoleAccess(...) now invokes authMethod with an extra param.getClientIp() argument. On 4.20, ConsoleProxyResource.authenticateConsoleAccess(...) only accepts 7 arguments, so this call will fail if/when authMethod is resolved. Keep the invocation arguments consistent with the reflected method signature (or implement a dual-signature invocation path).