This is an automated email from the ASF dual-hosted git repository.
dockerzhang pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/inlong.git
The following commit(s) were added to refs/heads/master by this push:
new a0f8ae14c7 [INLONG-12153][Manager] Add parameter validation for node
in clusterController (#12153)
a0f8ae14c7 is described below
commit a0f8ae14c7aafaeeceb81798d6bd144ef8af03b4
Author: fuweng11 <[email protected]>
AuthorDate: Thu Jul 9 09:35:28 2026 +0800
[INLONG-12153][Manager] Add parameter validation for node in
clusterController (#12153)
Co-authored-by: wakefu <[email protected]>
---
.../manager/pojo/cluster/ClusterNodeRequest.java | 19 ++++++++++-
.../service/cluster/InlongClusterServiceImpl.java | 38 ++++++++++++++++++++++
.../web/controller/InlongClusterController.java | 2 ++
3 files changed, 58 insertions(+), 1 deletion(-)
diff --git
a/inlong-manager/manager-pojo/src/main/java/org/apache/inlong/manager/pojo/cluster/ClusterNodeRequest.java
b/inlong-manager/manager-pojo/src/main/java/org/apache/inlong/manager/pojo/cluster/ClusterNodeRequest.java
index 14eaa7d984..c2ff1511c1 100644
---
a/inlong-manager/manager-pojo/src/main/java/org/apache/inlong/manager/pojo/cluster/ClusterNodeRequest.java
+++
b/inlong-manager/manager-pojo/src/main/java/org/apache/inlong/manager/pojo/cluster/ClusterNodeRequest.java
@@ -25,8 +25,11 @@ import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import org.hibernate.validator.constraints.Length;
+import javax.validation.constraints.Max;
+import javax.validation.constraints.Min;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
+import javax.validation.constraints.Pattern;
/**
* Inlong cluster node request
@@ -51,19 +54,33 @@ public class ClusterNodeRequest {
@ApiModelProperty(value = "Cluster IP")
@NotBlank(message = "ip cannot be blank")
- @Length(max = 512, message = "length must be less than or equal to 512")
+ @Length(max = 255, message = "ip length must be less than or equal to 255")
+ @Pattern(regexp = "^(?!-)"
+ + "(?:"
+ + "(?:[0-9]{1,3}\\.){3}[0-9]{1,3}"
+ + "|\\[[0-9a-fA-F:]+\\]"
+ + "|[0-9a-fA-F:]+"
+ + "|(?:[A-Za-z0-9](?:[A-Za-z0-9-]{0,61}[A-Za-z0-9])?)"
+ + "(?:\\.(?:[A-Za-z0-9](?:[A-Za-z0-9-]{0,61}[A-Za-z0-9])?))*"
+ + ")$", message = "ip must be a valid IPv4/IPv6/hostname and must
not start with '-'")
private String ip;
@ApiModelProperty(value = "Cluster port")
+ @Min(value = 1, message = "port must be between 1 and 65535")
+ @Max(value = 65535, message = "port must be between 1 and 65535")
private Integer port;
@ApiModelProperty(value = "Username")
+ @Length(max = 64, message = "username length must be less than or equal to
64")
+ @Pattern(regexp = "^$|^[A-Za-z0-9_][A-Za-z0-9_.@-]{0,63}$", message =
"username must not start with '-' and can only contain letters, digits, '_',
'.', '@' or '-'")
private String username;
@ApiModelProperty(value = "password")
private String password;
@ApiModelProperty(value = "SSH port")
+ @Min(value = 1, message = "sshPort must be between 1 and 65535")
+ @Max(value = 65535, message = "sshPort must be between 1 and 65535")
private Integer sshPort;
@ApiModelProperty(value = "Cluster protocol type")
diff --git
a/inlong-manager/manager-service/src/main/java/org/apache/inlong/manager/service/cluster/InlongClusterServiceImpl.java
b/inlong-manager/manager-service/src/main/java/org/apache/inlong/manager/service/cluster/InlongClusterServiceImpl.java
index 5985724f2b..6161b7e05f 100644
---
a/inlong-manager/manager-service/src/main/java/org/apache/inlong/manager/service/cluster/InlongClusterServiceImpl.java
+++
b/inlong-manager/manager-service/src/main/java/org/apache/inlong/manager/service/cluster/InlongClusterServiceImpl.java
@@ -857,6 +857,11 @@ public class InlongClusterServiceImpl implements
InlongClusterService {
public Boolean updateNode(ClusterNodeRequest request, String operator) {
LOGGER.debug("begin to update inlong cluster node={}", request);
Preconditions.expectNotNull(request, "inlong cluster node cannot be
empty");
+
+ // Same defense as saveNode: reject unsafe/internal ip before the
+ // install lane can reach the CommandExecutor sink.
+ validateClusterNodeIp(request);
+
Integer id = request.getId();
InlongClusterNodeEntity entity = clusterNodeMapper.selectById(id);
if (entity == null) {
@@ -961,6 +966,39 @@ public class InlongClusterServiceImpl implements
InlongClusterService {
}
}
+ /**
+ * Validate that the cluster node ip/host does not target internal/private
+ * networks before an install request is queued for the CommandExecutor.
+ *
+ * <p>Format-level checks (non-blank, length, no leading '-', character
+ * whitelist) are enforced declaratively on {@link ClusterNodeRequest#ip}
+ * via {@code @NotBlank}, {@code @Length} and {@code @Pattern}, so this
+ * method only carries the semantic SSRF check that cannot be expressed
+ * as an annotation.
+ *
+ * <p>Only enforced for AGENT-type nodes that actually go through the
+ * install lane, and only when {@code isInstall} is true, so existing
+ * flows for other cluster node types remain unaffected.
+ */
+ private void validateClusterNodeIp(ClusterNodeRequest request) {
+ if (request == null || !Boolean.TRUE.equals(request.getIsInstall())) {
+ return;
+ }
+ if (!ClusterType.AGENT.equalsIgnoreCase(request.getType())) {
+ return;
+ }
+ String ip = request.getIp();
+ if (StringUtils.isBlank(ip)) {
+ return;
+ }
+ try {
+ UrlVerificationUtils.validateHostNotInternal(ip);
+ } catch (Exception e) {
+ throw new BusinessException(ErrorCodeEnum.INVALID_PARAMETER,
+ "SSRF protection: " + e.getMessage());
+ }
+ }
+
@Override
public DataProxyNodeResponse getDataProxyNodes(String groupId, String
protocolType) {
LOGGER.debug("begin to get data proxy nodes for groupId={},
protocol={}", groupId, protocolType);
diff --git
a/inlong-manager/manager-web/src/main/java/org/apache/inlong/manager/web/controller/InlongClusterController.java
b/inlong-manager/manager-web/src/main/java/org/apache/inlong/manager/web/controller/InlongClusterController.java
index 23f2f0d7fd..9ebe381b4c 100644
---
a/inlong-manager/manager-web/src/main/java/org/apache/inlong/manager/web/controller/InlongClusterController.java
+++
b/inlong-manager/manager-web/src/main/java/org/apache/inlong/manager/web/controller/InlongClusterController.java
@@ -238,6 +238,7 @@ public class InlongClusterController {
@PostMapping(value = "/cluster/node/save")
@ApiOperation(value = "Save cluster node")
@OperationLog(operation = OperationType.CREATE, operationTarget =
OperationTarget.CLUSTER_NODE)
+ @RequiresRoles(UserRoleCode.INLONG_ADMIN)
public Response<Integer> saveNode(@Validated @RequestBody
ClusterNodeRequest request) {
String currentUser = LoginUserUtils.getLoginUser().getName();
request.setCurrentUser(currentUser);
@@ -275,6 +276,7 @@ public class InlongClusterController {
@RequestMapping(value = "/cluster/node/update", method =
RequestMethod.POST)
@OperationLog(operation = OperationType.UPDATE, operationTarget =
OperationTarget.CLUSTER_NODE)
@ApiOperation(value = "Update cluster node")
+ @RequiresRoles(UserRoleCode.INLONG_ADMIN)
public Response<Boolean> updateNode(@Validated(UpdateValidation.class)
@RequestBody ClusterNodeRequest request) {
String username = LoginUserUtils.getLoginUser().getName();
request.setCurrentUser(username);