2024 年 Clojure 状态调查!中分享您的看法。

欢迎!请参阅关于页面了解该网站如何工作的一些更多信息。

+1
Clojure CLI 通过

大家好,

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

无法从中央仓库 transfers artifact ...
(https://repo1.maven.org/maven2/)
sun.security.validator.ValidatorException: PKIX 路径构建失败
sun.security.provider.certpath.SunCertPathBuilderException: 无法
找到到请求目标的有效认证路径

我认为这是由于防火墙使用的自签名根证书所致,该证书用于控制与监视流量。

以下是分析内容,抱歉篇幅较长。


在大组织中,工作站通常位于防火墙后面,该防火墙控制并监视所有来自或前往互联网的流量。公司也会创建自己的自签名根证书,并用它替换 https 流量中找到的证书,以解密和分析数据流,从而利用中间人攻击来达到目的。

为此,发起连接的用户工作站需要在他们的受信任证书存储中安装自签名证书。在 Windows 中,这将是受信任根授权证书密钥库。

Java 自带其证书存储,即 java KeyStore,它与工作站上可用的常规密钥库分开。因此,任何通过防火墙连接到互联网的 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.*属性(请参阅https://stackoverflow.com/questions/5871279/ssl-and-cert-keystore)来指导java查找通用或备用的密钥库更容易。

在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. 让负责下载依赖项的clojure.tools.deps.alpha.script.make-classpath2函数在Windows上运行时设置javax.net.ssl.trustStoreType=Windows-ROOT,如果运行在Windows上
    • .例如,(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),其中的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:我也尝试过按照https://maven.apache.org/guides/mini/guide-repository-ssl.html将属性设置在MAVEN_OPTS变量中,但没有任何效果。我怀疑这只有在直接调用mvn命令行工具时才会生效。

1 答案

+1

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

我们已经在https://clojure.atlassian.net/browse/TDEPS-165上为这个问题创建了一个现有的工单,但我还没有着手处理它。我怀疑最终解决方案将是打开一个新的clj选项或环境变量,以便将jvm属性传递给类路径构造java调用。

看起来很好,但要注意,如我先前评论中提到的,不常规的值可能会对java调用产生不利影响。对于我的测试案例,它运行良好。

请问第二个新的JAVA_OPTS环境变量的目的是什么,java调用为什么在这和CLJ_JVM_OPTS之间分开?我原本预计只有一个CLJ_JVM_OPTS环境变量会被传递给所有java调用。

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

谢谢
好的,现在已作为1.11.1.1165发布。

有两个属性,因为有两个上下文(可能)有不同的需求和很多你可能希望在其中一个但不在另一个上设置属性的场景。
谢谢!

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

通过设置 PowerShell 命令提示符中的环境变量来使用 Windows 证书存储下载项目依赖项

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

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

$env:JAVA_OPTS = "-Djavax.net.ssl.trustStoreType=Windows-ROOT"
大家好,

希望能够参与到这个帖子中。这里也有类似的问题。企业防火墙执行 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 没有检测到它。

关于如何调试这个问题,有什么建议吗?

谢谢
我认为这个 stackoverflow 回答 https://stackoverflow.com/a/5871352/7671 对可能需要设置的属性有一个很好的概述,并且您可以通过 CLJ_JVM_OPTS 设置它们。
...