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

Reply via email to