Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 41 additions & 0 deletions core/src/main/java/org/zstack/core/Platform.java
Original file line number Diff line number Diff line change
Expand Up @@ -1387,6 +1387,47 @@ public static String getRouteSourceIp(String remoteIp) {
return null;
}

public static String getManagementServerIpForRemote(String remoteIp) {
return selectManagementServerIpForRemote(remoteIp, null);
}

public static String selectManagementServerIpForRemote(String remoteIp, String routeSourceIp) {
if (StringUtils.isBlank(remoteIp)) {
return getManagementServerIp();
}

remoteIp = normalizeManagementIp(remoteIp);
routeSourceIp = normalizeManagementIp(routeSourceIp);
if (IPv6NetworkUtils.isIpv6Address(remoteIp)) {
if (IPv6NetworkUtils.isIpv6Address(routeSourceIp) && isManagementServerIp(routeSourceIp)) {
return routeSourceIp;
}
String ip6 = getManagementServerIp6();
return ip6 == null ? getManagementServerIp() : ip6;
}

if (NetworkUtils.isIpv4Address(remoteIp)) {
if (NetworkUtils.isIpv4Address(routeSourceIp) && isManagementServerIp(routeSourceIp)) {
return routeSourceIp;
}
String ip4 = getManagementServerIp4();
return ip4 == null ? getManagementServerIp() : ip4;
}

return getManagementServerIp();
}

private static boolean isManagementServerIp(String ip) {
if (StringUtils.isBlank(ip)) {
return false;
}

String normalizedIp = normalizeManagementIp(ip);
return getManagementServerIps().stream()
.map(Platform::normalizeManagementIp)
.anyMatch(normalizedIp::equals);
}

public static String selectManagementServerIp(Collection<InetAddress> addresses) {
String ipv4 = null;
String ipv6 = null;
Expand Down
29 changes: 28 additions & 1 deletion core/src/main/java/org/zstack/core/rest/RESTFacadeImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -50,12 +50,14 @@
import org.zstack.utils.gson.JSONObjectUtil;
import org.zstack.utils.logging.CLogger;
import org.zstack.utils.network.IPv6NetworkUtils;
import org.zstack.utils.network.NetworkUtils;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedReader;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
Expand Down Expand Up @@ -227,6 +229,31 @@ public static String buildCallbackUrl(String hostName, int port, String path) {
return ub.build().toUriString();
}

public static String selectCallbackUrl(String requestUrl, Map<String, String> headers, String defaultCallbackUrl, int port, String path) {
if (headers != null && headers.keySet().stream().anyMatch(RESTConstant.CALLBACK_URL::equalsIgnoreCase)) {
return defaultCallbackUrl;
}

String host = extractRequestHost(requestUrl);
if (host == null) {
return defaultCallbackUrl;
}

if (!NetworkUtils.isIpv4Address(host) && !IPv6NetworkUtils.isIpv6Address(host)) {
return defaultCallbackUrl;
}

return buildCallbackUrl(Platform.getManagementServerIpForRemote(host), port, path);
}

private static String extractRequestHost(String requestUrl) {
try {
return IPv6NetworkUtils.stripHostUrlBrackets(new URI(requestUrl).getHost());
} catch (URISyntaxException | IllegalArgumentException e) {
return null;
}
}

public static String buildSendCommandUrl(String hostName, int port, String path) {
UriComponentsBuilder ub = UriComponentsBuilder.fromHttpUrl(buildBaseUrl(hostName, port, path));
ub.path(RESTConstant.COMMAND_CHANNEL_PATH);
Expand Down Expand Up @@ -415,7 +442,7 @@ public void asyncJson(final String url, final String body, Map<String, String> h
HttpHeaders requestHeaders = new HttpHeaders();
requestHeaders.setContentLength(body.length());
requestHeaders.set(RESTConstant.TASK_UUID, taskUuid);
requestHeaders.set(RESTConstant.CALLBACK_URL, callbackUrl);
requestHeaders.set(RESTConstant.CALLBACK_URL, selectCallbackUrl(url, headers, callbackUrl, port, path));
MediaType JSON = MediaType.parseMediaType("application/json; charset=utf-8");
requestHeaders.setContentType(JSON);
if (headers != null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,7 @@ public void run(final FlowTrigger trigger, Map data) {
callbackChecker.setUsername(getSelf().getSshUsername());
callbackChecker.setPassword(getSelf().getSshPassword());
callbackChecker.setPort(getSelf().getSshPort());
callbackChecker.setCallbackIp(Platform.getManagementServerIp());
callbackChecker.setCallbackIp(Platform.getManagementServerIpForRemote(getSelf().getHostname()));
callbackChecker.setCallBackPort(CloudBusGlobalProperty.HTTP_PORT);

AnsibleRunner runner = new AnsibleRunner();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,7 @@ public void run(final FlowTrigger trigger, Map data) {
callbackChecker.setUsername(getSelf().getSshUsername());
callbackChecker.setPassword(getSelf().getSshPassword());
callbackChecker.setPort(getSelf().getSshPort());
callbackChecker.setCallbackIp(Platform.getManagementServerIp());
callbackChecker.setCallbackIp(Platform.getManagementServerIpForRemote(getSelf().getHostname()));
callbackChecker.setCallBackPort(CloudBusGlobalProperty.HTTP_PORT);

AnsibleRunner runner = new AnsibleRunner();
Expand Down
4 changes: 2 additions & 2 deletions plugin/kvm/src/main/java/org/zstack/kvm/KVMHost.java
Original file line number Diff line number Diff line change
Expand Up @@ -6083,7 +6083,7 @@ public void run(final FlowTrigger trigger, Map data) {
callbackChecker.setUsername(getSelf().getUsername());
callbackChecker.setPassword(getSelf().getPassword());
callbackChecker.setPort(getSelf().getPort());
callbackChecker.setCallbackIp(Platform.getManagementServerIp());
callbackChecker.setCallbackIp(Platform.getManagementServerIpForRemote(getSelf().getManagementIp()));
callbackChecker.setCallBackPort(CloudBusGlobalProperty.HTTP_PORT);

KvmHostConfigChecker kvmHostConfigChecker = new KvmHostConfigChecker();
Expand All @@ -6107,7 +6107,7 @@ public void run(final FlowTrigger trigger, Map data) {
hostTcpConnectionCallbackChecker.setUsername(getSelf().getUsername());
hostTcpConnectionCallbackChecker.setPassword(getSelf().getPassword());
hostTcpConnectionCallbackChecker.setPort(getSelf().getPort());
hostTcpConnectionCallbackChecker.setCallbackIp(Platform.getManagementServerIp());
hostTcpConnectionCallbackChecker.setCallbackIp(Platform.getManagementServerIpForRemote(getSelf().getManagementIp()));
hostTcpConnectionCallbackChecker.setCallBackPort(KVMGlobalProperty.TCP_SERVER_PORT);
runner.installChecker(hostTcpConnectionCallbackChecker);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -392,7 +392,7 @@ private void connect(final Completion complete) {
callbackChecker.setUsername(getSelf().getUsername());
callbackChecker.setPassword(getSelf().getPassword());
callbackChecker.setPort(getSelf().getSshPort());
callbackChecker.setCallbackIp(Platform.getManagementServerIp());
callbackChecker.setCallbackIp(Platform.getManagementServerIpForRemote(getSelf().getHostname()));
callbackChecker.setCallBackPort(CloudBusGlobalProperty.HTTP_PORT);

AnsibleRunner runner = new AnsibleRunner();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ public void run(FlowTrigger trigger, Map data) {
callBackChecker.setUsername(getSelf().getUsername());
callBackChecker.setPassword(getSelf().getPassword());
callBackChecker.setPort(getSelf().getPort());
callBackChecker.setCallbackIp(Platform.getManagementServerIp());
callBackChecker.setCallbackIp(Platform.getManagementServerIpForRemote(getSelf().getAddr()));
callBackChecker.setCallBackPort(CloudBusGlobalProperty.HTTP_PORT);

AnsibleRunner runner = new AnsibleRunner();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,26 @@ class ManagementNetworkIpv6Case extends SubCase {
"http://[2001:db8::1]:8080/zstack${RESTConstant.COMMAND_CHANNEL_PATH}"
}

void testRestFacadeSelectsCallbackUrlByTargetIpVersion() {
withManagementServerIpProperties([
"management.server.ip" : IPV4,
"management.server.ip6": IPV6,
]) {
String defaultCallbackUrl = "http://${IPV4}:${REST_PORT}/zstack${RESTConstant.CALLBACK_PATH}"

assert RESTFacadeImpl.selectCallbackUrl(
"http://[${IPV6_2}]:7070/host/ping", [:], defaultCallbackUrl, REST_PORT, "zstack") ==
"http://[${IPV6}]:${REST_PORT}/zstack${RESTConstant.CALLBACK_PATH}"
assert RESTFacadeImpl.selectCallbackUrl(
"http://host.example.com:7070/host/ping", [:], defaultCallbackUrl, REST_PORT, "zstack") ==
defaultCallbackUrl
assert RESTFacadeImpl.selectCallbackUrl(
"http://[${IPV6_2}]:7070/host/ping",
[(RESTConstant.CALLBACK_URL): "http://override.example.com/callback"],
defaultCallbackUrl, REST_PORT, "zstack") == defaultCallbackUrl
}
}

void testSshTargetUsesRawIpv6Host() {
assert SshShell.formatSshTarget("root", IPV4) == "root@192.168.1.10"
assert SshShell.formatSshTarget("root", IPV6) == "root@2001:db8::1"
Expand Down Expand Up @@ -357,6 +377,25 @@ class ManagementNetworkIpv6Case extends SubCase {
}
}

void testSelectManagementServerIpForRemote() {
withManagementServerIpProperties([
"management.server.ip" : IPV4,
"management.server.ip6": IPV6,
]) {
assert Platform.selectManagementServerIpForRemote(IPV6_2, null) == IPV6
assert Platform.selectManagementServerIpForRemote(IPV6_2, "2001:db8::88") == IPV6
assert Platform.selectManagementServerIpForRemote("host.example.com", null) == IPV4
}

withManagementServerIpProperties([
"management.server.ip" : IPV6,
"management.server.ip4": IPV4,
]) {
assert Platform.selectManagementServerIpForRemote("192.168.1.20", null) == IPV4
assert Platform.selectManagementServerIpForRemote("192.168.1.20", "192.168.1.88") == IPV4
}
}

void testManagementServerSecondaryPropertyRejectsWrongAddressFamily() {
withManagementServerIpProperties([
"management.server.ip" : IPV4,
Expand Down