【问题标题】:Secure Debugging for Production JVMs生产 JVM 的安全调试
【发布时间】:2009-05-28 19:36:12
【问题描述】:

我们有一些应用程序有时会进入不良状态,但仅限于生产环境(当然!)。虽然进行堆转储有助于收集状态信息,但使用远程调试器通常更容易。设置它很容易——只需将它添加到他的命令行中:

-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=PORT

似乎没有可用的安全机制,因此在生产中打开调试将有效地允许任意代码执行(通过热插拔)。

我们混合了 1.4.2 和 1.5 Sun JVM,在 Solaris 9 和 Linux (Redhat Enterprise 4) 上运行。我们如何启用安全调试?还有其他方法可以实现我们的生产服务器检查目标吗?

更新: 对于 JDK 1.5+ JVM,可以指定调试器应该绑定的接口和端口。因此,KarlP 建议绑定到环回并仅使用 SSH 隧道连接到本地开发人员框应该可以在服务器上正确设置 SSH 的情况下工作。

但是,JDK1.4x 似乎不允许为调试端口指定接口。那么,我们可以阻止对网络中某处的调试端口的访问,或者在操作系统本身中进行一些特定于系统的阻止(Jared 建议的 IPChains 等)?

更新 #2:这是一个可以让我们限制风险的 hack,即使在 1.4.2 JVM 上也是如此:

命令行参数:

-Xdebug
-Xrunjdwp:
    transport=dt_socket,
    server=y,
    suspend=n,
    address=9001,
    onthrow=com.whatever.TurnOnDebuggerException,
    launch=nothing

开启调试器的Java代码:

try {
    throw new TurnOnDebuggerException();
} catch (TurnOnDebugger td) {
   //Nothing
}

TurnOnDebuggerException 可以是保证不会在其他任何地方抛出的任何异常。

我在 Windows 机器上对此进行了测试,以证明 (1) 调试器端口最初不接收连接,以及 (2) 如上所示抛出 TurnOnDebugger 异常会导致调试器激活。启动参数是必需的(至少在 JDK1.4.2 上),但是 JVM 可以优雅地处理垃圾值。

我们正计划制作一个小型 servlet,在适当的安全性之后,它可以让我们打开调试器。当然,之后不能关闭它,并且调试器一旦打开它仍然会乱听。但是,这些是我们愿意接受的限制,因为生产系统的调试总是会导致之后重新启动。

更新 #3: 我最终编写了三个类:(1) TurnOnDebuggerException,一个普通的 Java 异常,(2) DebuggerPoller,一个后台线程,用于检查指定文件是否存在在文件系统上,以及 (3) DebuggerMainWrapper,一个启动轮询线程然后反射性地调用另一个指定类的 main 方法的类。

这是它的使用方式:

  1. 在启动脚本中用 DebuggerMainWrapper 替换“主”类
  2. 添加两个系统 (-D) 参数,一个指定真正的主类,另一个指定文件系统上的文件。
  3. 在命令行上配置调试器,添加 onthrow=com.whatever.TurnOnDebuggerException 部分
  4. 将包含上述三个类的 jar 添加到类路径中。

现在,当您启动 JVM 时,除了启动了一个后台轮​​询线程之外,一切都相同。假设该文件(我们的称为 TurnOnDebugger)最初不存在,轮询器每 N 秒检查一次。当轮询器第一次注意到它时,它会抛出并立即捕获 TurnOnDebuggerException。然后,代理被启动。

您无法将其重新关闭,而且机器在打开时也不是很安全。从好的方面来说,我不认为调试器允许多个同时连接,所以保持调试连接是你最好的防御。我们选择文件通知方法是因为它允许我们通过在只有正确使用权限的目录中指定触发器文件来搭载现有的 Unix authen/author。您可以轻松地构建一个通过套接字连接实现相同目的的小 war 文件。当然,由于我们无法关闭调试器,我们只会在关闭有病的应用程序之前使用它来收集数据。如果有人想要这个代码,请告诉我。但是,您只需几分钟就可以自己组装起来。

【问题讨论】:

    标签: java security debugging


    【解决方案1】:

    如果您使用 SSH,您可以允许隧道和隧道端口到您的本地主机。无需开发,全部使用 sshd、ssh 和/或 putty 完成。

    可以在本地接口 127.0.0.1 上设置 java 服务器上的调试套接字。

    【讨论】:

    • 如果这可行(现在正在测试),它似乎是我们的最佳选择。这不像我们定期调试,但我们确实希望能够捕获处于异常状态的 JVM。
    • 我认为这只适用于 JDK 1.5+:java.sun.com/j2se/1.5.0/docs/guide/jpda/enhancements.html 请参阅上述链接中的“dt_socket 传输已修改为在服务器模式下运行时采用本地地址”。
    • @Shabby - 是的 - 看起来这可以工作 1.5+,并且是一个很好的解决方案。另一种方法是通过防火墙(软件或硬件)锁定调试端口。也许为您的 linux 主机检查 ipchains? (tldp.org/HOWTO/IPCHAINS-HOWTO.html)
    • 我们的一位基础架构人员指出,绑定到环回限制了对相关机器具有权限的访问权限——对于某些应用程序来说可能有点宽泛。但是,这在很多情况下可能已经足够了。
    【解决方案2】:

    您完全正确:Java 调试 API 本质上是不安全的。但是,您可以将其限制为 UNIX 域套接字,并使用 SSL/SSH 编写代理,让您拥有经过身份验证和加密的外部连接,然后将其代理到 UNIX 域套接字中。这至少可以减少您接触到可以将进程进入服务器的人,或者可以破解您的 SSL 的人。

    【讨论】:

    • 可以将默认接口上的端口映射到域套接字吗?我遇到的问题(我在最初的帖子后发现)是 1.4.x Sun JVM 只能绑定到默认(?)接口。因此,需要一些神奇的映射,这样该端口就不会暴露在 VM 之外。
    【解决方案3】:

    将信息/服务导出到 JMX 中,然后使用 RMI+SSL 远程访问它。您的情况就是 JMX 的设计目的(M 代表管理)。

    【讨论】:

    • 我同意应该通过 JMX 公开通用指标。我们实际上是在使用一个轻量级的生产分析器(Wily),但它在捕获状态信息方面不是很好,并且只能在受限于粗粒度的跟踪时表现良好。另一个问题是其中一些应用程序(部分)是第三方的,因此我们最多只能使用反编译的源代码进行调试。
    • 我仍然认为将调试器连接到生产应用程序是一个坏主意。当您遇到断点并花一些时间在内存中查找时,用户将不知道发生了什么。我会尝试在您的代码中找到有问题的地方,并通过 JMX 公开当前状态,并保留正在发生的事情的详细审计日志。
    • 我们实际上会在调试之前将表现不佳的实例从负载平衡集群中移除。我同意调试一个有活跃用户的应用是一个非常糟糕的主意。
    【解决方案4】:

    好问题。

    我不知道有任何内置功能可以加密与调试端口的连接。

    可能有更好/更简单的解决方案,但我会执行以下操作:

    1. 将生产机器置于防火墙后面,阻止对调试端口的访问。
    2. 在连接到端口的主机本身上运行代理进程,并加密来自套接字的输入和输出。
    3. 在调试工作站上运行一个代理客户端,它也加密/解密输入。将此连接到服务器代理。它们之间的通信将被加密。
    4. 将您的调试器连接到代理客户端。

    【讨论】:

    • 附带说明:我们的生产服务器位于防火墙后面,但它们暴露在某些内部网段中。
    猜你喜欢
    • 2013-06-24
    • 2019-09-05
    • 2011-07-19
    • 1970-01-01
    • 2012-10-24
    • 1970-01-01
    • 1970-01-01
    • 2013-11-10
    • 2011-06-30
    相关资源
    最近更新 更多