diff --git a/core/src/main/java/arthas.properties b/core/src/main/java/arthas.properties index e562eea80..98457d5ba 100644 --- a/core/src/main/java/arthas.properties +++ b/core/src/main/java/arthas.properties @@ -12,6 +12,9 @@ arthas.sessionTimeout=1800 # arthas.username=arthas # arthas.password=arthas +# local connection non auth, like telnet 127.0.0.1 3658 +arthas.localConnectionNonAuth=true + #arthas.appName=demoapp #arthas.tunnelServer=ws://127.0.0.1:7777/ws #arthas.agentId=mmmmmmyiddddd diff --git a/core/src/main/java/com/taobao/arthas/core/config/Configure.java b/core/src/main/java/com/taobao/arthas/core/config/Configure.java index 433b22131..14780a0d7 100644 --- a/core/src/main/java/com/taobao/arthas/core/config/Configure.java +++ b/core/src/main/java/com/taobao/arthas/core/config/Configure.java @@ -10,7 +10,11 @@ import java.util.Map; import static java.lang.reflect.Modifier.isStatic; /** - * 配置类 + *
+ * 配置类。
+ * 注意本类里的所有字段不能有默认值,否则会出现配置混乱。
+ * 在 com.taobao.arthas.core.Arthas#attach 里会调用 Configure#toStrig
+ * 
  *
  * @author vlinux
  * @author hengyunabc 2018-11-12
@@ -65,6 +69,11 @@ public class Configure {
      */
     private String disabledCommands;
 
+    /**
+     * 本地连接不需要鉴权,即使配置了password。arthas.properties 里默认为true
+     */
+    private Boolean localConnectionNonAuth;
+
     public String getIp() {
         return ip;
     }
@@ -193,6 +202,14 @@ public class Configure {
         this.disabledCommands = disabledCommands;
     }
 
+    public boolean isLocalConnectionNonAuth() {
+        return localConnectionNonAuth != null && localConnectionNonAuth;
+    }
+
+    public void setLocalConnectionNonAuth(boolean localConnectionNonAuth) {
+        this.localConnectionNonAuth = localConnectionNonAuth;
+    }
+
     /**
      * 序列化成字符串
      *
diff --git a/core/src/main/java/com/taobao/arthas/core/security/AuthUtils.java b/core/src/main/java/com/taobao/arthas/core/security/AuthUtils.java
new file mode 100644
index 000000000..7a9895782
--- /dev/null
+++ b/core/src/main/java/com/taobao/arthas/core/security/AuthUtils.java
@@ -0,0 +1,37 @@
+package com.taobao.arthas.core.security;
+
+import java.net.InetSocketAddress;
+import java.net.SocketAddress;
+import java.security.Principal;
+
+import com.taobao.arthas.core.config.Configure;
+import com.taobao.arthas.core.server.ArthasBootstrap;
+
+import io.netty.channel.ChannelHandlerContext;
+
+/**
+ * 
+ * @author hengyunabc 2021-09-01
+ *
+ */
+public class AuthUtils {
+    private static Configure configure = ArthasBootstrap.getInstance().getConfigure();
+
+    public static Principal localPrincipal(ChannelHandlerContext ctx) {
+        if (configure.isLocalConnectionNonAuth() && isLocalConnection(ctx)) {
+            return new LocalConnectionPrincipal();
+        }
+        return null;
+    }
+
+    public static boolean isLocalConnection(ChannelHandlerContext ctx) {
+        SocketAddress remoteAddress = ctx.channel().remoteAddress();
+        if (remoteAddress instanceof InetSocketAddress) {
+            String hostAddress = ((InetSocketAddress) remoteAddress).getAddress().getHostAddress();
+            if ("127.0.0.1".equals(hostAddress)) {
+                return true;
+            }
+        }
+        return false;
+    }
+}
diff --git a/core/src/main/java/com/taobao/arthas/core/security/LocalConnectionPrincipal.java b/core/src/main/java/com/taobao/arthas/core/security/LocalConnectionPrincipal.java
new file mode 100644
index 000000000..06b8e1d88
--- /dev/null
+++ b/core/src/main/java/com/taobao/arthas/core/security/LocalConnectionPrincipal.java
@@ -0,0 +1,27 @@
+package com.taobao.arthas.core.security;
+
+import java.security.Principal;
+
+/**
+ * 本地连接的特殊处理 {@link Principal}.
+ * 
+ * @author hengyunabc 2021-09-01
+ */
+public final class LocalConnectionPrincipal implements Principal {
+
+    public LocalConnectionPrincipal() {
+    }
+
+    @Override
+    public String getName() {
+        return null;
+    }
+
+    public String getUsername() {
+        return null;
+    }
+
+    public String getPassword() {
+        return null;
+    }
+}
\ No newline at end of file
diff --git a/core/src/main/java/com/taobao/arthas/core/security/SecurityAuthenticatorImpl.java b/core/src/main/java/com/taobao/arthas/core/security/SecurityAuthenticatorImpl.java
index e43bd19d8..88b6f4425 100644
--- a/core/src/main/java/com/taobao/arthas/core/security/SecurityAuthenticatorImpl.java
+++ b/core/src/main/java/com/taobao/arthas/core/security/SecurityAuthenticatorImpl.java
@@ -66,6 +66,9 @@ public class SecurityAuthenticatorImpl implements SecurityAuthenticator {
                 return subject;
             }
         }
+        if (principal instanceof LocalConnectionPrincipal) {
+            return subject;
+        }
 
         return null;
     }
diff --git a/core/src/main/java/com/taobao/arthas/core/server/ArthasBootstrap.java b/core/src/main/java/com/taobao/arthas/core/server/ArthasBootstrap.java
index 2ffd72bac..7b4dabb5e 100644
--- a/core/src/main/java/com/taobao/arthas/core/server/ArthasBootstrap.java
+++ b/core/src/main/java/com/taobao/arthas/core/server/ArthasBootstrap.java
@@ -665,4 +665,7 @@ public class ArthasBootstrap {
         return securityAuthenticator;
     }
 
+    public Configure getConfigure() {
+        return configure;
+    }
 }
diff --git a/core/src/main/java/com/taobao/arthas/core/shell/impl/ShellImpl.java b/core/src/main/java/com/taobao/arthas/core/shell/impl/ShellImpl.java
index f69002adf..3e00e984a 100644
--- a/core/src/main/java/com/taobao/arthas/core/shell/impl/ShellImpl.java
+++ b/core/src/main/java/com/taobao/arthas/core/shell/impl/ShellImpl.java
@@ -2,6 +2,10 @@ package com.taobao.arthas.core.shell.impl;
 
 import com.alibaba.arthas.deps.org.slf4j.Logger;
 import com.alibaba.arthas.deps.org.slf4j.LoggerFactory;
+import com.taobao.arthas.common.ArthasConstants;
+import com.taobao.arthas.core.security.AuthUtils;
+import com.taobao.arthas.core.security.SecurityAuthenticator;
+import com.taobao.arthas.core.server.ArthasBootstrap;
 import com.taobao.arthas.core.shell.Shell;
 import com.taobao.arthas.core.shell.ShellServer;
 import com.taobao.arthas.core.shell.cli.CliToken;
@@ -22,10 +26,15 @@ import com.taobao.arthas.core.shell.term.impl.http.ExtHttpTtyConnection;
 import com.taobao.arthas.core.util.Constants;
 import com.taobao.arthas.core.util.FileUtils;
 
+import io.netty.channel.ChannelHandlerContext;
+import io.termd.core.telnet.TelnetConnection;
+import io.termd.core.telnet.TelnetTtyConnection;
+import io.termd.core.telnet.netty.NettyTelnetConnection;
 import io.termd.core.tty.TtyConnection;
 
 import java.io.File;
 import java.lang.instrument.Instrumentation;
+import java.security.Principal;
 import java.util.Date;
 import java.util.List;
 import java.util.Map;
@@ -33,14 +42,17 @@ import java.util.Map.Entry;
 import java.util.Set;
 import java.util.UUID;
 
+import javax.security.auth.Subject;
+import javax.security.auth.login.LoginException;
+
 /**
  * The shell session as seen from the shell server perspective.
  *
  * @author Julien Viet
  */
 public class ShellImpl implements Shell {
-
     private static final Logger logger = LoggerFactory.getLogger(ShellImpl.class);
+    private SecurityAuthenticator securityAuthenticator = ArthasBootstrap.getInstance().getSecurityAuthenticator();
 
     private JobControllerImpl jobController;
     final String id;
@@ -57,6 +69,26 @@ public class ShellImpl implements Shell {
         if (term instanceof TermImpl) {
             TermImpl termImpl = (TermImpl) term;
             TtyConnection conn = termImpl.getConn();
+            // 处理telnet本地连接鉴权
+            if (conn instanceof TelnetTtyConnection) {
+                TelnetConnection telnetConnection = ((TelnetTtyConnection) conn).getTelnetConnection();
+                if (telnetConnection instanceof NettyTelnetConnection) {
+                    ChannelHandlerContext handlerContext = ((NettyTelnetConnection) telnetConnection)
+                            .channelHandlerContext();
+                    Principal principal = AuthUtils.localPrincipal(handlerContext);
+                    if (principal != null) {
+                        try {
+                            Subject subject = securityAuthenticator.login(principal);
+                            if (subject != null) {
+                                session.put(ArthasConstants.SUBJECT_KEY, subject);
+                            }
+                        } catch (LoginException e) {
+                            logger.error("local connection auth error", e);
+                        }
+                    }
+                }
+            }
+
             if (conn instanceof ExtHttpTtyConnection) {
                 // 传递http cookie 里的鉴权信息到新建立的session中
                 ExtHttpTtyConnection extConn = (ExtHttpTtyConnection) conn;
diff --git a/core/src/main/java/com/taobao/arthas/core/shell/term/impl/http/BasicHttpAuthenticatorHandler.java b/core/src/main/java/com/taobao/arthas/core/shell/term/impl/http/BasicHttpAuthenticatorHandler.java
index b14975cd1..f19491e56 100644
--- a/core/src/main/java/com/taobao/arthas/core/shell/term/impl/http/BasicHttpAuthenticatorHandler.java
+++ b/core/src/main/java/com/taobao/arthas/core/shell/term/impl/http/BasicHttpAuthenticatorHandler.java
@@ -1,6 +1,7 @@
 package com.taobao.arthas.core.shell.term.impl.http;
 
 import java.nio.charset.Charset;
+import java.security.Principal;
 import java.util.List;
 import java.util.Map;
 
@@ -9,6 +10,7 @@ import javax.security.auth.Subject;
 import com.alibaba.arthas.deps.org.slf4j.Logger;
 import com.alibaba.arthas.deps.org.slf4j.LoggerFactory;
 import com.taobao.arthas.common.ArthasConstants;
+import com.taobao.arthas.core.security.AuthUtils;
 import com.taobao.arthas.core.security.BasicPrincipal;
 import com.taobao.arthas.core.security.SecurityAuthenticator;
 import com.taobao.arthas.core.server.ArthasBootstrap;
@@ -64,18 +66,23 @@ public final class BasicHttpAuthenticatorHandler extends ChannelDuplexHandler {
                 authed = true;
             }
 
+            Principal principal = null;
             if (!authed) {
                 // 判断请求header里是否带有 username/password
-                BasicPrincipal principal = extractBasicAuthSubject(httpRequest);
+                principal = extractBasicAuthSubject(httpRequest);
                 if (principal == null) {
                     // 判断 url里是否有 username/password
                     principal = extractBasicAuthSubjectFromUrl(httpRequest);
                 }
-                Subject subject = securityAuthenticator.login(principal);
-                if (subject != null) {
-                    authed = true;
-                    session.setAttribute(ArthasConstants.SUBJECT_KEY, subject);
-                }
+            }
+            if (!authed && principal == null) {
+                // 判断是否本地连接
+                principal = AuthUtils.localPrincipal(ctx);
+            }
+            Subject subject = securityAuthenticator.login(principal);
+            if (subject != null) {
+                authed = true;
+                session.setAttribute(ArthasConstants.SUBJECT_KEY, subject);
             }
 
             if (!authed) {
diff --git a/pom.xml b/pom.xml
index 1e1052850..51662234d 100644
--- a/pom.xml
+++ b/pom.xml
@@ -102,7 +102,7 @@
             
                 com.alibaba.middleware
                 termd-core
-                1.1.7.11
+                1.1.7.12
             
             
                 com.alibaba.middleware
diff --git a/site/src/site/sphinx/auth.md b/site/src/site/sphinx/auth.md
index 739dab790..35dc3ada8 100644
--- a/site/src/site/sphinx/auth.md
+++ b/site/src/site/sphinx/auth.md
@@ -19,6 +19,16 @@ java -jar arthas-boot.jar --password ppp
   Using generated security password: 0vUBJpRIppkKuZ7dYzYqOKtranj4unGh
   ```
 
+### 本地连接不鉴权
+
+默认情况下,在`arthas.properties`文件里有配置:
+
+```
+arthas.localConnectionNonAuth=true
+```
+
+当配置密码时,使用本地连接,也不需要鉴权。默认配置值是true,方便本地连接使用。只有远程连接时,才需要鉴权。
+
 ### 在telnet console里鉴权
 
 连接到arthas后,直接执行命令会提示需要鉴权:
diff --git a/site/src/site/sphinx/en/auth.md b/site/src/site/sphinx/en/auth.md
index 937e3aa9e..5b11af66d 100644
--- a/site/src/site/sphinx/en/auth.md
+++ b/site/src/site/sphinx/en/auth.md
@@ -19,6 +19,14 @@ java -jar arthas-boot.jar --password ppp
   Using generated security password: 0vUBJpRIppkKuZ7dYzYqOKtranj4unGh
   ```
 
+### Local connection does not require authentication
+By default, there are configurations in the `arthas.properties` file:
+
+```
+arthas.localConnectionNonAuth=true
+```
+When the password is configured, connect from localhost, the authentication is not required. The default configuration value is true, which is convenient for local connection. Authentication is only required when connecting remotely.
+
 ### Authenticate in the telnet console
 
 After connecting to arthas, directly executing the command will prompt for authentication: