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 e3d2b8dffa [INLONG-10261][Manager] Support installing agents by SSH
key-based authentication (#10390)
e3d2b8dffa is described below
commit e3d2b8dffa96720c51aa7c53dea816d0ab5d6574
Author: qingliu <[email protected]>
AuthorDate: Thu Jun 13 16:33:56 2024 +0800
[INLONG-10261][Manager] Support installing agents by SSH key-based
authentication (#10390)
---
.../service/cluster/InlongClusterService.java | 13 +++++++++
.../service/cluster/InlongClusterServiceImpl.java | 22 +++++++++++++++
.../manager/service/cmd/CommandExecutor.java | 2 ++
.../manager/service/cmd/CommandExecutorImpl.java | 24 +++++++++++++++-
.../service/cmd/shell/ShellExecutorImpl.java | 13 ++-------
.../web/controller/InlongClusterController.java | 6 ++++
.../manager-web/src/main/resources/exec_cmd.exp | 8 ++++--
.../resources/{exec_cmd.exp => ssh_key_cmd.exp} | 32 +++++++++-------------
8 files changed, 88 insertions(+), 32 deletions(-)
diff --git
a/inlong-manager/manager-service/src/main/java/org/apache/inlong/manager/service/cluster/InlongClusterService.java
b/inlong-manager/manager-service/src/main/java/org/apache/inlong/manager/service/cluster/InlongClusterService.java
index 2696d76afb..56c153bfd4 100644
---
a/inlong-manager/manager-service/src/main/java/org/apache/inlong/manager/service/cluster/InlongClusterService.java
+++
b/inlong-manager/manager-service/src/main/java/org/apache/inlong/manager/service/cluster/InlongClusterService.java
@@ -400,6 +400,19 @@ public interface InlongClusterService {
*/
Boolean deleteNode(Integer id, UserInfo opInfo);
+ /**
+ * Retrieves the SSH public key from the manager node for agent
installation.
+ *
+ * <p>To enable SSH key-based authentication, the manager node's public
key must be added to the agent node.
+ * This method either generates a new public key or retrieves an existing
one.</p>
+ *
+ * <p>After obtaining the SSH public key, add it to the
<code>~/.ssh/authorized_keys</code> file on the agent node.
+ * This allows the manager node to execute remote commands on the agent
node via SSH.</p>
+ *
+ * @return the SSH public key as a String
+ */
+ String getManagerSSHPublicKey();
+
/**
* Query data proxy nodes by the given inlong group id and 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 4f7589e181..41e1df631d 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
@@ -77,6 +77,7 @@ import
org.apache.inlong.manager.service.cluster.node.InlongClusterNodeInstallOp
import
org.apache.inlong.manager.service.cluster.node.InlongClusterNodeInstallOperatorFactory;
import
org.apache.inlong.manager.service.cluster.node.InlongClusterNodeOperator;
import
org.apache.inlong.manager.service.cluster.node.InlongClusterNodeOperatorFactory;
+import org.apache.inlong.manager.service.cmd.CommandExecutor;
import org.apache.inlong.manager.service.repository.DataProxyConfigRepository;
import
org.apache.inlong.manager.service.repository.DataProxyConfigRepositoryV2;
import org.apache.inlong.manager.service.tenant.InlongTenantService;
@@ -99,6 +100,9 @@ import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Transactional;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
@@ -146,6 +150,8 @@ public class InlongClusterServiceImpl implements
InlongClusterService {
private TenantRoleService tenantRoleService;
@Autowired
private InlongClusterNodeInstallOperatorFactory
clusterNodeInstallOperatorFactory;
+ @Autowired
+ private CommandExecutor commandExecutor;
@Lazy
@Autowired
@@ -1206,6 +1212,22 @@ public class InlongClusterServiceImpl implements
InlongClusterService {
return true;
}
+ @Override
+ public String getManagerSSHPublicKey() {
+ String homeDirectory = System.getProperty("user.home");
+ String publicKeyPath = homeDirectory + "/.ssh/inlong_rsa.pub";
+ try {
+ Path path = Paths.get(publicKeyPath);
+ if (!Files.exists(path)) {
+ commandExecutor.execSSHKeyGeneration();
+ }
+ return StringUtils.strip(new String(Files.readAllBytes(path)),
"\n");
+ } catch (Exception e) {
+ LOGGER.error("get manager ssh public key error", e);
+ throw new RuntimeException("get manager ssh public key error", e);
+ }
+ }
+
@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-service/src/main/java/org/apache/inlong/manager/service/cmd/CommandExecutor.java
b/inlong-manager/manager-service/src/main/java/org/apache/inlong/manager/service/cmd/CommandExecutor.java
index e7f8eac0d8..ef44719d7e 100644
---
a/inlong-manager/manager-service/src/main/java/org/apache/inlong/manager/service/cmd/CommandExecutor.java
+++
b/inlong-manager/manager-service/src/main/java/org/apache/inlong/manager/service/cmd/CommandExecutor.java
@@ -25,6 +25,8 @@ public interface CommandExecutor {
CommandResult exec(String cmd) throws Exception;
+ CommandResult execSSHKeyGeneration() throws Exception;
+
CommandResult execRemote(AgentClusterNodeRequest clusterNodeRequest,
String cmd) throws Exception;
CommandResult modifyConfig(AgentClusterNodeRequest clusterNodeRequest,
Map<String, String> configMap,
diff --git
a/inlong-manager/manager-service/src/main/java/org/apache/inlong/manager/service/cmd/CommandExecutorImpl.java
b/inlong-manager/manager-service/src/main/java/org/apache/inlong/manager/service/cmd/CommandExecutorImpl.java
index b1729b9b9a..1ce4069fe1 100644
---
a/inlong-manager/manager-service/src/main/java/org/apache/inlong/manager/service/cmd/CommandExecutorImpl.java
+++
b/inlong-manager/manager-service/src/main/java/org/apache/inlong/manager/service/cmd/CommandExecutorImpl.java
@@ -54,6 +54,25 @@ public class CommandExecutorImpl implements CommandExecutor {
return commandResult;
}
+ @Override
+ public CommandResult execSSHKeyGeneration() throws Exception {
+ String cmdShell = "./conf/ssh_key_cmd.exp";
+ ShellTracker shellTracker = new ShellTracker();
+ ShellExecutorImpl shellExecutor = new ShellExecutorImpl(shellTracker);
+ shellExecutor.syncExec(cmdShell);
+
+ CommandResult commandResult = new CommandResult();
+ commandResult.setCode(shellTracker.getCode());
+ commandResult.setResult(String.join(InlongConstants.BLANK,
shellTracker.getResult()));
+ LOG.debug(commandResult.getResult());
+ if (commandResult.getCode() != 0) {
+ throw new Exception("SSH key generation failed, code = " +
+ commandResult.getCode() + ", output = " +
commandResult.getResult());
+ }
+ LOG.debug(commandResult.getResult());
+ return commandResult;
+ }
+
@Override
public CommandResult execRemote(AgentClusterNodeRequest
clusterNodeRequest, String cmd) throws Exception {
String cmdShell = "./conf/exec_cmd.exp";
@@ -62,7 +81,10 @@ public class CommandExecutorImpl implements CommandExecutor {
String user = clusterNodeRequest.getUsername();
String password = clusterNodeRequest.getPassword();
String remoteCommandTimeout = "20000";
-
+ // If the password is null, set the password to an empty string, and
then use the public key for identification.
+ if (StringUtils.isBlank(password)) {
+ password = "";
+ }
cmd = "sh -c \"" + cmd + "\"";
String cmdMsg =
String.join(InlongConstants.BLANK, cmdShell, ip, user,
password, remoteCommandTimeout, cmd, port);
diff --git
a/inlong-manager/manager-service/src/main/java/org/apache/inlong/manager/service/cmd/shell/ShellExecutorImpl.java
b/inlong-manager/manager-service/src/main/java/org/apache/inlong/manager/service/cmd/shell/ShellExecutorImpl.java
index 6b910684ca..24642a523e 100644
---
a/inlong-manager/manager-service/src/main/java/org/apache/inlong/manager/service/cmd/shell/ShellExecutorImpl.java
+++
b/inlong-manager/manager-service/src/main/java/org/apache/inlong/manager/service/cmd/shell/ShellExecutorImpl.java
@@ -20,12 +20,12 @@ package org.apache.inlong.manager.service.cmd.shell;
import org.apache.inlong.manager.common.consts.InlongConstants;
import lombok.extern.slf4j.Slf4j;
-import org.apache.commons.lang3.StringUtils;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.lang.reflect.Field;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -56,15 +56,8 @@ public class ShellExecutorImpl implements ShellExecutor {
private static String[] merge(String shellPath, String[] paths) {
List<String> cmds = new ArrayList<String>();
cmds.add(shellPath);
- for (String path : paths) {
- if (StringUtils.isBlank(path)) {
- continue;
- }
- cmds.add(path);
- }
- String[] strings = new String[cmds.size()];
- cmds.toArray(strings);
- return strings;
+ cmds.addAll(Arrays.asList(paths));
+ return cmds.toArray(new String[0]);
}
private static String arrayToString(Object[] array, String split) {
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 f80383d02f..da6bc39253 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
@@ -297,6 +297,12 @@ public class InlongClusterController {
return Response.success(clusterService.unloadNode(id,
LoginUserUtils.getLoginUser().getName()));
}
+ @RequestMapping(value = "/cluster/node/getManagerSSHPublicKey", method =
RequestMethod.GET)
+ @ApiOperation(value = "Obtain the SSH public key from the manager to
install the agent.")
+ public Response<String> getManagerSSHPublicKey() {
+ return Response.success(clusterService.getManagerSSHPublicKey());
+ }
+
@PostMapping("/cluster/testConnection")
@ApiOperation(value = "Test connection for inlong cluster")
public Response<Boolean> testConnection(@Validated @RequestBody
ClusterRequest request) {
diff --git a/inlong-manager/manager-web/src/main/resources/exec_cmd.exp
b/inlong-manager/manager-web/src/main/resources/exec_cmd.exp
index a56079f96d..b177ed41b2 100644
--- a/inlong-manager/manager-web/src/main/resources/exec_cmd.exp
+++ b/inlong-manager/manager-web/src/main/resources/exec_cmd.exp
@@ -24,8 +24,12 @@ set cmdTimeout [lindex $argv 3]
set runCommand [lindex $argv 4]
set remotePort [lindex $argv 5]
-spawn ssh -p ${remotePort} ${remoteHost} -l ${remoteUser} "${runCommand} &&
echo \\#SUCCESS\\# || echo \\#fail\\#"
-
+# use the public key to identify
+if { $password == "" } {
+ spawn ssh -p ${remotePort} -i ~/.ssh/inlong_rsa ${remoteHost} -l
${remoteUser} "${runCommand} && echo \\#SUCCESS\\# || echo \\#fail\\#"
+} else {
+ spawn ssh -p ${remotePort} ${remoteHost} -l ${remoteUser} "${runCommand}
&& echo \\#SUCCESS\\# || echo \\#fail\\#"
+}
set timeout ${cmdTimeout}
expect {
"*yes/no)?" {send "yes\n"; exp_continue}
diff --git a/inlong-manager/manager-web/src/main/resources/exec_cmd.exp
b/inlong-manager/manager-web/src/main/resources/ssh_key_cmd.exp
similarity index 55%
copy from inlong-manager/manager-web/src/main/resources/exec_cmd.exp
copy to inlong-manager/manager-web/src/main/resources/ssh_key_cmd.exp
index a56079f96d..04412adbd5 100644
--- a/inlong-manager/manager-web/src/main/resources/exec_cmd.exp
+++ b/inlong-manager/manager-web/src/main/resources/ssh_key_cmd.exp
@@ -17,25 +17,19 @@
# specific language governing permissions and limitations
# under the License.
#
-set remoteHost [lindex $argv 0]
-set remoteUser [lindex $argv 1]
-set password [lindex $argv 2]
-set cmdTimeout [lindex $argv 3]
-set runCommand [lindex $argv 4]
-set remotePort [lindex $argv 5]
-
-spawn ssh -p ${remotePort} ${remoteHost} -l ${remoteUser} "${runCommand} &&
echo \\#SUCCESS\\# || echo \\#fail\\#"
+spawn /bin/sh -c "echo ~"
+expect {
+ -re "(.*)\r\n" {
+ set home_dir $expect_out(1,string)
+ }
+}
+expect eof
-set timeout ${cmdTimeout}
+spawn ssh-keygen -t rsa -b 4096 -f ${home_dir}/.ssh/inlong_rsa
expect {
- "*yes/no)?" {send "yes\n"; exp_continue}
- "*assword:" {send "${password}\n"; exp_continue }
- "Last login:" {}
- "#SUCCESS#" {}
- "#fail#" {exit 1 }
- "*No route to host" {exit 2}
- "Permission denied" {exit 3}
- "*Host key verification failed*" {exit 4}
- timeout { exit 5 }
- eof { exit 6 }
+ "*Enter file in which to save the key" {send "\r"; exp_continue}
+ "Overwrite (y/n)?" { send "n\r"; exp_continue }
+ "Enter passphrase (empty for no passphrase):" { send "\r"; exp_continue }
+ "Enter same passphrase again:" { send "\r"; exp_continue }
+ eof
}
\ No newline at end of file