请分享您的想法(2024 年 Clojure 状态调查!

欢迎!请查看关于页面了解有关此操作的更多信息。

+1
Clojure CLI

嗨,

当我尝试在 MS-Windows 的严格防火墙后面使用 clojure 工具下载项目依赖项时,我遇到了 PKIX 认证异常。

无法从 central 转移工件...
(https://repo1.maven.org/maven2/)
sun.security.validator.ValidatorException: PKIX 路径构建失败
sun.security.provider.certpath.SunCertPathBuilderException: unable
find valid certification path to requested target

我认为这是由于防火墙使用的自签名根证书控制的流量引发的。

以下是一些分析和道歉,由于这篇长文。


在大组织中,工作站通常会位于防火墙后面,该防火墙监控所有与互联网的流量。公司也常创建自己的自签名根证书,并将其替换为 https 流量中找到的证书,以便解密和分析数据流,实际上利用中间人攻击。

为此,用户工作站(连接源自)需要在其受信任的证书存储中安装自签名证书。在 Windows 中,这将是从工作站可用的通用证书存储库中。

Java 附带自己的证书存储,java 密钥存储库,它与工作站上可用的通用密钥存储库分开。因此,任何从防火墙通过互联网进行的 https 连接很可能因未知证书错误而失败,因为自签名证书未安装到 Java 密钥存储库中。

这对于 clojure/clj 命令行工具下载库不利,因为它无法在 Java 密钥存储库中找到自签名证书。例如,从这样的计算机尝试访问互联网时会抛出异常。

 > clojure -P
 
 Error building classpath ... 
 org.eclipse.aether.resolution.ArtifactDescriptorException:   Failed to
 read artifact descriptor for ....  ...  Caused by:
 org.eclipse.aether.resolution.ArtifactResolutionException:   Could not
 transfer artifact ... from/to central
 (https://repo1.maven.org/maven2/):   
 sun.security.validator.ValidatorException: PKIX path building failed:
      sun.security.provider.certpath.SunCertPathBuilderException:
        unable to find valid certification path to requested target  ...
  ...

我想至少有两种方法可以解决此问题
1. 在 Java 密钥存储库中安装缺失的证书,
2. 指示 Java 实例检查通用密钥存储库。

似乎没有直接的方法来发现缺失的证书,也许需要用户自己使用 java keytool 将它们安装到 Java 密钥存储库中(请参阅https://docs.oracle.com/cd/E19830-01/819-4712/ablqw/index.html中的证书和 SSL 工作原理)。

相反,似乎通过使用java.next.ssl.*属性来指导 java 读取通用或备用密钥库更容易(详情请参考以下链接:https://stackoverflow.com/questions/5871279/ssl-and-cert-keystore)。

在 Windows 上,我认为大多数拥有如此严格的防火墙的用户都使用 Windows 本地密钥库,只需简单地设置一个 java 属性来指导 java 使用通用 Windows 密钥库(详情请参考以下链接:https://stackoverflow.com/questions/41257366/import-windows-certificates-to-java

javax.net.ssl.trustStoreType=Windows-ROOT

鉴于这种情况,我原本期望在 Windows 上以下代码能够工作

clojure -J-D'javax.net.ssl.trustStoreType=Windows-ROOT' -P

但事实并非如此。结果显示$JvmOpts变量在此 PowerShell 脚本中没有传递到预备命令中https://github.com/clojure/brew-install/blob/b91fb78e321b5e39bedda594f5d578579d448d19/src/main/resources/clojure/install/ClojureTools.psm1#L388

& $JavaCmd -classpath $ToolsCp clojure.main -m clojure.tools.deps.alpha.script.make-classpath2 --config-user $ConfigUser --config-project $ConfigProject --basis-file $BasisFile --libs-file $LibsFile --cp-file $CpFile --jvm-file $JvmFile --main-file $MainFile --manifest-file $ManifestFile @ToolsArgs

以下是我考虑使用 java 属性来解决这个问题的一些选项,从最具侵入性/最具体到通用情况

  1. 如果运行在 Windows 上,负责下载依赖的函数clojure.tools.deps.alpha.script.make-classpath2应设置javax.net.ssl.trustStoreType=Windows-ROOT,这样就可以在运行时使用
    • 例如:(System/setProperty "javax.net.ssl.trustStoreType" "Windows-ROOT")
    • cons
      • 这仅在 Windows 上有效。
      • 这会强制使用通用密钥库,这可能并非用户一直所期望的。
        • 可以通过添加一个新脚本选项(例如-W)来减轻这一点,该选项将作为参数传递给clojure.tools.deps.alpha。缺点是用户/工具在每个调用时都必须传递此标志。
      • 使用System/setProperty在运行时设置javax.net.ssl.tustStoreType属性可能没有效果,如果有任何 SSL 连接是在代码的其他部分之前建立的。
  2. 与上一个选项相同,但引入一个新脚本选项(例如-Sssl),其中 EDN 是javax.net.ssl.*属性,并将其作为此类传递给clojure.tools.deps.alpha,以便在运行时进行设置。
    • 例如:clojure -Sssl {:trustStoreType "Windows-ROOT"} -P
    • cons
      • 与第 1 点有相同的缺点,并且对于用户来说更加繁琐,每次调用 clojure 时都必须输入 edn 映射。
  3. 创造一个新的 deps.edn 键(例如::clojure.tools.deps.alpha/ssl),其配对将是java.net.ssl.*属性,可以在用户配置的 deps.edn 中设置,并由 clojure.tools.deps.alphamake-classpath2 函数解析,在运行时使用 System/setProperty 设置属性。
    • 例如,示例 clj 用户配置 {:clojure.tools.deps.alpha/ssl {:trustStore "Windows-ROOT"}}
    • cons
      • 使用System/setProperty在运行时设置javax.net.ssl.*属性可能没有效果,如果有任何 SSL 连接是在代码的其他部分之前建立的。
  4. 更新脚本,以便将 -J JVM 选项传递到 -P 命令的 java 调用中(我已经测试过这个可以工作)。
    • 例如:clojure -J-D'javax.net.ssl.trustStoreType=Windows-ROOT' -P
    • cons
      • 用户/工具仍然必须在每次 clojure 调用时提供-J-Djavax.net.ssl.*选项。
  5. 与上一个选项相同,但引入一个环境变量CLJ_JVMOPTS,其值插入脚本中的$JvmOpts变量。
    • 例如:set CLJ_JVMOPTS=-D'javax.net.ssl.trustStoreType=Windows-ROOT',然后clojure -P

我更倾向于第 5 点,因为它似乎没有任何缺点,并且可以设置一次并在所有 clojure 调用中都应用,为用户/工具提供更好的体验。

让我知道你的想法。我很乐意处理脚本和/或 tools.deps.alpha 库中的更改。

谢谢

PS:我还尝试在 MAVEN_OPTS 变量中设置属性,按照https://maven.apache.org/guides/mini/guide-repository-ssl.html 进行,但没有任何效果。我怀疑这只有在直接调用 mvn 命令行工具时才会生效。

1 答案

+1

我认为这是所有正确的分析,我们在一些情况下遇到了可能需要在生成类路径的java调用上设置jvm属性的几个情况,正如你所发现的,这些属性目前无法设置。

我们已经在https://clojure.atlassian.net/browse/TDEPS-165建立了关于此问题的现有工单,但我也还没有着手处理。我怀疑最终的解决方案将是提供一个新的clj选项或环境变量,允许将jvm属性传递给类路径构造java调用。

我应该继续创建一个原型补丁,引入一个新的CLJ_JVMOPTS环境变量,该变量将合并到标准的 -J 选项中,并传递到脚本中的所有java调用中吗?

或者是否担心将jvm选项传递给所有的java调用,而不仅仅是用于类路径构造/下载的调用?
我对这个问题还没有想得足够清楚,所以无法告诉你补丁应该做什么。我觉得将这些属性传递给每一个java调用(特别是用户的程序)是没有意义的。
你能否详细说明一下,为什么在本脚本中为每一个java调用传递jvm选项没有意义?我觉得没有明显的缺点。谢谢。
我已经更新了工单的方案,并且实际上实施了一个预发布版(1.11.1.1161),其中支持新的CLJ_JVM_OPTS。但目前还不支持Windows,欢迎任何关于在PowerShell版本中正确等效表达式的建议——对于https://github.com/clojure/brew-install/commit/d97cf4145ccfee712ec99b906f7bf19b1406c131的处理方式有点棘手,难点在于正确处理多个参数。
嗨,

我进行了一点点研究,了解如何将额外的jvm参数传递给java调用,我想出的唯一一个在大多数(所有?)情况下都能优雅处理的方法是通过`JAVA_TOOL_OPTIONS`环境变量传递。如果我们能将额外的参数添加到这个环境变量的末尾,那么它们将被java工具捕获并按预期进行处理。

我已经创建了一个草案实现以示例:[https://github.com/ikappaki/brew-install/commit/3584b02caced8e4a15c6993c73ceb4c202a6c623](https://github.com/ikappaki/brew-install/commit/3584b02caced8e4a15c6993c73ceb4c202a6c623)。这种方法的一个细微的实现复杂性是我们必须在java命令调用完成后恢复环境变量,以免其更新的值泄露到环境中。

我考虑的另一个选项是像`$CljJvmOpts = iex "echo $env:CLJ_JVM_OPTS"`一样解析环境变量,但这会与标准引号机制冲突,并且很难用引号传递。

将任何用户值传递给$java命令行调用的问题在于,我们可能会遇到不希望出现的情况,例如命令行中出现分号或其他Powershell操作符,这可能会破坏调用语法,更不用说它可能成为安全风险(例如,将环境变量设置为`;rm -fr ./;`。)

你对此有何看法?谢谢。
是的,我不是很喜欢这种方式。我已经添加了对Windows的支持,将环境变量在空格处拆分并在命令行中进行展开,这在尝试过的所有情况下工作得都相当不错。如果可以的话,版本号为1.11.1.1165可以试一下(安装程序在https://download.clojure.org/install/win-install-1.11.1.1165.ps1
在之前评论中提到,不太常规的值可能会对Java的调用产生不利影响,但是看起来效果很好。对于我的测试案例来说,它运行良好。

我想了解一下第二个新的JAVA_OPTS环境变量的用途,以及为什么Java调用在这两者之间分开?我期望所有的Java调用都会通过一个单一的CLJ_JVM_OPTS环境变量传递。

https://github.com/clojure/brew-install/commit/7914954030ca21f7b928b6f064ace086efdc9057

谢谢
好的,现在它已经发布为1.11.1.1165了。

有两个属性,因为有两个具有(可能)不同需求的环境,还有许多你可能希望在一个上有属性但在另一个上没有属性的案例。
谢谢!

以下是如何使用新的环境变量来解决此用例的示例,供有兴趣的人参考。

在PowerShell提示符中设置环境变量,以便在下载项目依赖项时使用Windows证书存储

$env:CLJ_JVM_OPTS = "-Djavax.net.ssl.trustStoreType=Windows-ROOT"

要在通过Clojure Tools运行项目时使用Windows证书存储,请在PowerShell提示符中设置环境变量

$env:JAVA_OPTS = "-Djavax.net.ssl.trustStoreType=Windows-ROOT"
by
嗨,

希望我能参与到这个问题中来。我这里也有类似的问题。企业防火墙进行了SSL检查并替换SSL证书(ZScaler)。我最简单的选择是将证书安装到CA存储中。

"%JAVA_HOME%\bin\keytool.exe" -import -trustcacerts -cacerts -storepass XXX -file somefile.cer

我可以用https://github.com/MichalHecko/SSLPoke 确认它是否工作
> java.exe SSLPoke download.clojure.org 443
成功连接

但是,在安装Clojure依赖项时,我遇到了与OP相同的问题(PKIX路径构建失败:sun.security.provider.certpath.SunCertPathBuilderException:无法找到请求目标的有效证书路径)

我认为我不需要使用Windows-ROOT信任存储。我只需要默认的Java CA存储(在$JAVA_HOME/lib/security/cacerts中),这应该可以工作,但看起来Clojure没有检测到这一点。

关于如何调试这个问题的任何建议吗?

谢谢
by
我认为这个StackOverflow回答https://stackoverflow.com/a/5871352/7671 对可能需要设置的性质有很好的概述,并通过CLJ_JVM_OPTS设置。
...