Skip to content

Commit df4103f

Browse files
authored
novnc: Add source IP check (#4736)
* novnc: Add client IP check for novnc console in cloudstack 4.16 * novnc ip check : Fix restart CPVM or mgt server does not update novnc param * novnc ip check: move to method
1 parent cdc3b08 commit df4103f

12 files changed

Lines changed: 81 additions & 9 deletions

File tree

agent/src/main/java/com/cloud/agent/resource/consoleproxy/ConsoleProxyResource.java

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ public Answer executeRequest(final Command cmd) {
113113

114114
private Answer execute(StartConsoleProxyAgentHttpHandlerCommand cmd) {
115115
s_logger.info("Invoke launchConsoleProxy() in responding to StartConsoleProxyAgentHttpHandlerCommand");
116-
launchConsoleProxy(cmd.getKeystoreBits(), cmd.getKeystorePassword(), cmd.getEncryptorPassword());
116+
launchConsoleProxy(cmd.getKeystoreBits(), cmd.getKeystorePassword(), cmd.getEncryptorPassword(), cmd.isSourceIpCheckEnabled());
117117
return new Answer(cmd);
118118
}
119119

@@ -313,7 +313,7 @@ public String getName() {
313313
return _name;
314314
}
315315

316-
private void launchConsoleProxy(final byte[] ksBits, final String ksPassword, final String encryptorPassword) {
316+
private void launchConsoleProxy(final byte[] ksBits, final String ksPassword, final String encryptorPassword, final Boolean isSourceIpCheckEnabled) {
317317
final Object resource = this;
318318
s_logger.info("Building class loader for com.cloud.consoleproxy.ConsoleProxy");
319319
if (_consoleProxyMain == null) {
@@ -325,8 +325,8 @@ protected void runInContext() {
325325
Class<?> consoleProxyClazz = Class.forName("com.cloud.consoleproxy.ConsoleProxy");
326326
try {
327327
s_logger.info("Invoke startWithContext()");
328-
Method method = consoleProxyClazz.getMethod("startWithContext", Properties.class, Object.class, byte[].class, String.class, String.class);
329-
method.invoke(null, _properties, resource, ksBits, ksPassword, encryptorPassword);
328+
Method method = consoleProxyClazz.getMethod("startWithContext", Properties.class, Object.class, byte[].class, String.class, String.class, Boolean.class);
329+
method.invoke(null, _properties, resource, ksBits, ksPassword, encryptorPassword, isSourceIpCheckEnabled);
330330
} catch (SecurityException e) {
331331
s_logger.error("Unable to launch console proxy due to SecurityException", e);
332332
System.exit(ExitStatus.Error.value());
@@ -358,6 +358,8 @@ protected void runInContext() {
358358
Class<?> consoleProxyClazz = Class.forName("com.cloud.consoleproxy.ConsoleProxy");
359359
Method methodSetup = consoleProxyClazz.getMethod("setEncryptorPassword", String.class);
360360
methodSetup.invoke(null, encryptorPassword);
361+
methodSetup = consoleProxyClazz.getMethod("setIsSourceIpCheckEnabled", Boolean.class);
362+
methodSetup.invoke(null, isSourceIpCheckEnabled);
361363
} catch (SecurityException e) {
362364
s_logger.error("Unable to launch console proxy due to SecurityException", e);
363365
System.exit(ExitStatus.Error.value());

core/src/main/java/com/cloud/agent/api/proxy/StartConsoleProxyAgentHttpHandlerCommand.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ public class StartConsoleProxyAgentHttpHandlerCommand extends Command {
3131
@LogLevel(Log4jLevel.Off)
3232
private String encryptorPassword;
3333

34+
private Boolean isSourceIpCheckEnabled;
35+
3436
public StartConsoleProxyAgentHttpHandlerCommand() {
3537
super();
3638
}
@@ -68,4 +70,12 @@ public String getEncryptorPassword() {
6870
public void setEncryptorPassword(String encryptorPassword) {
6971
this.encryptorPassword = encryptorPassword;
7072
}
73+
74+
public Boolean isSourceIpCheckEnabled() {
75+
return isSourceIpCheckEnabled;
76+
}
77+
78+
public void setIsSourceIpCheckEnabled(Boolean isSourceIpCheckEnabled) {
79+
this.isSourceIpCheckEnabled = isSourceIpCheckEnabled;
80+
}
7181
}

server/src/main/java/com/cloud/api/ApiServlet.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -389,7 +389,7 @@ private boolean isSpecificAPI(String commandName) {
389389
}
390390

391391
//This method will try to get login IP of user even if servlet is behind reverseProxy or loadBalancer
392-
static InetAddress getClientAddress(final HttpServletRequest request) throws UnknownHostException {
392+
public static InetAddress getClientAddress(final HttpServletRequest request) throws UnknownHostException {
393393
for(final String header : s_clientAddressHeaders) {
394394
final String ip = getCorrectIPAddress(request.getHeader(header));
395395
if (ip != null) {

server/src/main/java/com/cloud/consoleproxy/AgentHookBase.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,7 @@ public void startAgentHttpHandlerInVM(StartupProxyCommand startupCmd) {
208208

209209
cmd = new StartConsoleProxyAgentHttpHandlerCommand(ksBits, storePassword);
210210
cmd.setEncryptorPassword(getEncryptorPassword());
211+
cmd.setIsSourceIpCheckEnabled(Boolean.parseBoolean(_configDao.getValue(ConsoleProxyManager.NoVncConsoleSourceIpCheckEnabled.key())));
211212

212213
HostVO consoleProxyHost = findConsoleProxyHost(startupCmd);
213214

server/src/main/java/com/cloud/consoleproxy/ConsoleProxyManager.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,9 @@ public interface ConsoleProxyManager extends Manager, ConsoleProxyService {
4141
public static final ConfigKey<Boolean> NoVncConsoleDefault = new ConfigKey<Boolean>("Advanced", Boolean.class, "novnc.console.default", "true",
4242
"If true, noVNC console will be default console for virtual machines", true);
4343

44+
public static final ConfigKey<Boolean> NoVncConsoleSourceIpCheckEnabled = new ConfigKey<Boolean>("Advanced", Boolean.class, "novnc.console.sourceip.check.enabled", "false",
45+
"If true, The source IP to access novnc console must be same as the IP in request to management server for console URL. Needs to reconnect CPVM to management server when this changes (via restart CPVM, or management server, or cloud service in CPVM)", false);
46+
4447
public void setManagementState(ConsoleProxyManagementState state);
4548

4649
public ConsoleProxyManagementState getManagementState();

server/src/main/java/com/cloud/consoleproxy/ConsoleProxyManagerImpl.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1755,7 +1755,7 @@ public String getConfigComponentName() {
17551755

17561756
@Override
17571757
public ConfigKey<?>[] getConfigKeys() {
1758-
return new ConfigKey<?>[] { NoVncConsoleDefault };
1758+
return new ConfigKey<?>[] { NoVncConsoleDefault, NoVncConsoleSourceIpCheckEnabled };
17591759
}
17601760

17611761
}

server/src/main/java/com/cloud/servlet/ConsoleProxyClientParam.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ public class ConsoleProxyClientParam {
3333
private String username;
3434
private String password;
3535

36+
private String sourceIP;
37+
3638
public ConsoleProxyClientParam() {
3739
clientHostPort = 0;
3840
}
@@ -140,4 +142,12 @@ public void setPassword(String password) {
140142
public String getPassword() {
141143
return password;
142144
}
145+
146+
public String getSourceIP() {
147+
return sourceIP;
148+
}
149+
150+
public void setSourceIP(String sourceIP) {
151+
this.sourceIP = sourceIP;
152+
}
143153
}

server/src/main/java/com/cloud/servlet/ConsoleProxyServlet.java

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,9 @@
1717
package com.cloud.servlet;
1818

1919
import java.io.IOException;
20+
import java.net.InetAddress;
2021
import java.net.URLEncoder;
22+
import java.net.UnknownHostException;
2123
import java.util.ArrayList;
2224
import java.util.Collections;
2325
import java.util.Date;
@@ -46,6 +48,7 @@
4648
import com.google.gson.Gson;
4749
import com.google.gson.GsonBuilder;
4850

51+
import com.cloud.api.ApiServlet;
4952
import com.cloud.consoleproxy.ConsoleProxyManager;
5053
import com.cloud.exception.PermissionDeniedException;
5154
import com.cloud.host.HostVO;
@@ -289,8 +292,15 @@ private void handleAccessRequest(HttpServletRequest req, HttpServletResponse res
289292
}
290293
}
291294

295+
InetAddress remoteAddress = null;
296+
try {
297+
remoteAddress = ApiServlet.getClientAddress(req);
298+
} catch (UnknownHostException e) {
299+
s_logger.warn("UnknownHostException when trying to lookup remote IP-Address. This should never happen. Blocking request.", e);
300+
}
301+
292302
StringBuffer sb = new StringBuffer();
293-
sb.append("<html><title>").append(escapeHTML(vmName)).append("</title><frameset><frame src=\"").append(composeConsoleAccessUrl(rootUrl, vm, host));
303+
sb.append("<html><title>").append(escapeHTML(vmName)).append("</title><frameset><frame src=\"").append(composeConsoleAccessUrl(rootUrl, vm, host, remoteAddress));
294304
sb.append("\"></frame></frameset></html>");
295305
s_logger.debug("the console url is :: " + sb.toString());
296306
sendResponse(resp, sb.toString());
@@ -417,7 +427,7 @@ private String composeThumbnailUrl(String rootUrl, VirtualMachine vm, HostVO hos
417427
return sb.toString();
418428
}
419429

420-
private String composeConsoleAccessUrl(String rootUrl, VirtualMachine vm, HostVO hostVo) {
430+
private String composeConsoleAccessUrl(String rootUrl, VirtualMachine vm, HostVO hostVo, InetAddress addr) {
421431
StringBuffer sb = new StringBuffer(rootUrl);
422432
String host = hostVo.getPrivateIpAddress();
423433

@@ -465,6 +475,7 @@ private String composeConsoleAccessUrl(String rootUrl, VirtualMachine vm, HostVO
465475
param.setClientHostPassword(sid);
466476
param.setClientTag(tag);
467477
param.setTicket(ticket);
478+
param.setSourceIP(addr != null ? addr.getHostAddress(): null);
468479

469480
if (details != null) {
470481
param.setLocale(details.getValue());

services/console-proxy/server/src/main/java/com/cloud/consoleproxy/ConsoleProxy.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ public class ConsoleProxy {
5757
// dynamically changing to customer supplied certificate)
5858
public static byte[] ksBits;
5959
public static String ksPassword;
60+
public static Boolean isSourceIpCheckEnabled;
6061

6162
public static Method authMethod;
6263
public static Method reportMethod;
@@ -232,7 +233,7 @@ public static void ensureRoute(String address) {
232233
}
233234
}
234235

235-
public static void startWithContext(Properties conf, Object context, byte[] ksBits, String ksPassword, String password) {
236+
public static void startWithContext(Properties conf, Object context, byte[] ksBits, String ksPassword, String password, Boolean isSourceIpCheckEnabled) {
236237
setEncryptorPassword(password);
237238
configLog4j();
238239
Logger.setFactory(new ConsoleProxyLoggerFactory());
@@ -248,6 +249,7 @@ public static void startWithContext(Properties conf, Object context, byte[] ksBi
248249
ConsoleProxy.context = context;
249250
ConsoleProxy.ksBits = ksBits;
250251
ConsoleProxy.ksPassword = ksPassword;
252+
ConsoleProxy.isSourceIpCheckEnabled = isSourceIpCheckEnabled;
251253
try {
252254
final ClassLoader loader = Thread.currentThread().getContextClassLoader();
253255
Class<?> contextClazz = loader.loadClass("com.cloud.agent.resource.consoleproxy.ConsoleProxyResource");
@@ -526,6 +528,10 @@ public static void setEncryptorPassword(String password) {
526528
encryptorPassword = password;
527529
}
528530

531+
public static void setIsSourceIpCheckEnabled(Boolean isEnabled) {
532+
isSourceIpCheckEnabled = isEnabled;
533+
}
534+
529535
static class ThreadExecutor implements Executor {
530536
@Override
531537
public void execute(Runnable r) {

services/console-proxy/server/src/main/java/com/cloud/consoleproxy/ConsoleProxyClientParam.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ public class ConsoleProxyClientParam {
3737
private String username;
3838
private String password;
3939

40+
private String sourceIP;
41+
4042
public ConsoleProxyClientParam() {
4143
clientHostPort = 0;
4244
}
@@ -143,4 +145,12 @@ public void setPassword(String password) {
143145
public String getPassword() {
144146
return password;
145147
}
148+
149+
public String getSourceIP() {
150+
return sourceIP;
151+
}
152+
153+
public void setSourceIP(String sourceIP) {
154+
this.sourceIP = sourceIP;
155+
}
146156
}

0 commit comments

Comments
 (0)