2024年的Clojure现状调查!中分享您的想法。

欢迎您!请参阅关于页面了解该平台的工作方式的相关信息。

+1投票
Clojure CLI

你好,

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

无法从中央仓库传输工件 ... 到/从
(https://repo1.maven.org/maven2/)
sun.security.validator.ValidatorException: PKIX路径构建失败
sun.security.provider.certpath.SunCertPathBuilderException: unable
to find valid certification path to requested target

我认为这是由于防火墙利用自签名根证书来控制以及监控流量造成的。

以下为分析内容,因篇幅较长,敬请谅解。


在大公司中,将工作站置于防火墙后方是一个标准做法,该防火墙控制并监控来自互联网的所有流量。公司通常会创建他们自己的自签名根证书,并替换掉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密钥库中(有关证书和SSL的信息,请参阅https://docs.oracle.com/cd/E19830-01/819-4712/ablqw/index.html)。

相反,似乎更容易指导Java使用java.next.ssl.*属性来查看通用或备用密钥库(请参阅https://stackoverflow.com/questions/5871279/ssl-and-cert-keystore)。

在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 脚本中没有被传递给 prep 命令,如 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,.
    • .例如,使用(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:我还在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调用。

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

我可以询问第二个新的JAVA_OPTS环境变量的目的吗?为什么java调用会在JAVA_OPTS和CLJ_JVM_OPTS之间分割?我原本期望所有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"

通过在 PowerShell 提示符下设置环境变量,可以在使用 Clojure 工具运行项目时使用 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 依赖项时,我也遇到了同样的错误(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。
...